2023 lines
62 KiB
C++
2023 lines
62 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.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// textreportviewer.cpp -- implementation for textreportviewer class
|
|
//
|
|
|
|
// TODO:BAM put strings into resources
|
|
// TODO:BAM add debug strings
|
|
|
|
//=========================================================================
|
|
// DIRECTIVES
|
|
//=========================================================================
|
|
|
|
|
|
//=========================================================================
|
|
// INCLUDES
|
|
//=========================================================================
|
|
|
|
#include "stdtw.h"
|
|
#include "textreportviewer.h"
|
|
#include "core/errorbucketimpl.h"
|
|
#include "core/fsservices.h"
|
|
#include "core/timeconvert.h"
|
|
#include "core/msystem.h"
|
|
#include "core/twlocale.h"
|
|
#include "core/errortable.h"
|
|
#include "core/debug.h"
|
|
#include "core/errorutil.h"
|
|
#include "core/displayutil.h"
|
|
#include "core/fixedfilebuf.h"
|
|
#include "fco/fcospec.h"
|
|
#include "fco/iterproxy.h"
|
|
#include "fco/fconame.h"
|
|
#include "fco/fcopropvector.h"
|
|
#include "fco/fcopropset.h"
|
|
#include "fco/fcoprop.h"
|
|
#include "fco/fconametranslator.h"
|
|
#include "fco/fcoundefprop.h"
|
|
#include "fco/genreswitcher.h"
|
|
#include "fco/twfactory.h"
|
|
#include "fco/fcopropdisplayer.h"
|
|
#include "fco/fcospecattr.h"
|
|
#include "fco/fcoundefprop.h"
|
|
#include "core/stringutil.h"
|
|
#include "util/fileutil.h"
|
|
#include "headerinfo.h"
|
|
#include "fcoreport.h"
|
|
#include "twstrings.h"
|
|
#include "core/displayencoder.h"
|
|
|
|
//=========================================================================
|
|
// STANDARD LIBRARY INCLUDES
|
|
//=========================================================================
|
|
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <time.h>
|
|
#include <fcntl.h>
|
|
#ifdef HAVE_WCHAR_H
|
|
# include <wchar.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
//=========================================================================
|
|
// OTHER DIRECTIVES
|
|
//=========================================================================
|
|
|
|
using namespace std;
|
|
|
|
//=========================================================================
|
|
// GLOBALS
|
|
//=========================================================================
|
|
|
|
static const TCHAR* g_sz79Dashes = _T("-------------------------------------------------------------------------------");
|
|
static const TCHAR* g_sz40Dashes = _T("----------------------------------------");
|
|
static const TCHAR* g_sz79Equals = _T("===============================================================================");
|
|
|
|
//=========================================================================
|
|
// UTIL FUNCTION PROTOTYES
|
|
//=========================================================================
|
|
|
|
static void OpenOutputFile( fixed_basic_ofstream<TCHAR>& out, const TSTRING& strFile ); // throw( eTextReportViewer )
|
|
static void OpenInputFile ( std::ifstream& out, const TSTRING& strFile ); // throw( eTextReportViewer )
|
|
static bool PrintableProp( const iFCO* pfcoOld, const iFCO* pfcoNew, int j );
|
|
|
|
static bool SpecEmpty( const cFCOReportSpecIter& ri );
|
|
|
|
static TSTRING util_Encode( const TSTRING& sIn )
|
|
{
|
|
static cDisplayEncoder e;
|
|
TSTRING sOut = sIn;
|
|
e.Encode( sOut );
|
|
return sOut;
|
|
}
|
|
//=========================================================================
|
|
// METHOD CODE
|
|
//=========================================================================
|
|
|
|
cTextReportViewer::cTextReportViewer( const cFCOReportHeader& h, const cFCOReport& r )
|
|
{
|
|
Init( h, const_cast<cFCOReport&>(r) ); // TODO:BAM -- hack to allow creation
|
|
// for updating (non-const report) and non-updating
|
|
// (const report) in one constructor format
|
|
}
|
|
|
|
cTextReportViewer::cTextReportViewer( const cFCOReportHeader& h, cFCOReport& r )
|
|
{
|
|
Init( h, r );
|
|
}
|
|
|
|
int cTextReportViewer::Init( const cFCOReportHeader& h, cFCOReport& r )
|
|
{
|
|
mpHeader = &h;
|
|
mpReport = &r;
|
|
mpOut = NULL;
|
|
mpIn = NULL;
|
|
mpCurPD = NULL;
|
|
mpCurNT = NULL;
|
|
mfUpdate = false;
|
|
mMaxSeverityViolated = 0;
|
|
mNumberViolations = 0;
|
|
mErrorNum = 1;
|
|
mReportingLevel = FULL_REPORT;
|
|
mfGotNumbers = false;
|
|
mCurrentChar[0] = '\0';
|
|
mCurrentCharSize = 0;
|
|
return 0;
|
|
}
|
|
|
|
cTextReportViewer::~cTextReportViewer()
|
|
{
|
|
if( mfUpdate )
|
|
{
|
|
for( GenreList::iterator i = mFCOsRemoveFromReport.begin(); i != mFCOsRemoveFromReport.end(); ++i )
|
|
{
|
|
ASSERT( i->second != 0 );
|
|
i->second->clear();
|
|
delete i->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int cTextReportViewer::GetMaxSeverityViolated()
|
|
{
|
|
if( ! mfGotNumbers )
|
|
GetReportNumbers();
|
|
|
|
return mMaxSeverityViolated;
|
|
}
|
|
|
|
int cTextReportViewer::GetNumberViolations()
|
|
{
|
|
if( ! mfGotNumbers )
|
|
GetReportNumbers();
|
|
|
|
return mNumberViolations;
|
|
}
|
|
|
|
void cTextReportViewer::InitOStream()
|
|
{
|
|
// align left
|
|
(*mpOut).flags( ( (*mpOut).flags() & ~std::ios::adjustfield ) | std::ios::left );
|
|
}
|
|
|
|
void cTextReportViewer::DisplayReportAndHaveUserUpdateIt( const TSTRING& edName, ReportingLevel level ) //throw (eFSServices)
|
|
{
|
|
if (!CanUpdate())
|
|
{
|
|
// We should not be calling this for classes derived from cTextReport that can't update
|
|
THROW_INTERNAL("textreportviewer.cpp");
|
|
}
|
|
|
|
ASSERT( ! edName.empty() );
|
|
|
|
//
|
|
// get temp filename in which to store user-readable report
|
|
//
|
|
TSTRING strTempFile;
|
|
iFSServices* pFSServices = iFSServices::GetInstance();
|
|
ASSERT( pFSServices != 0 );
|
|
|
|
pFSServices->GetTempDirName( strTempFile );
|
|
strTempFile += _T("twtempXXXXXX");
|
|
pFSServices->MakeTempFilename( strTempFile );
|
|
|
|
// this try/catch ensures the temp file was removed...
|
|
try
|
|
{
|
|
// we are updating...
|
|
mfUpdate = true;
|
|
|
|
//
|
|
// write report to the temp file. put checks beside each FCO entry.
|
|
//
|
|
PrintTextReport( strTempFile, level );
|
|
|
|
//
|
|
// find an appropriate editor and launch it on the temp file. the user
|
|
// can uncheck any item she doesn't want put back into the database.
|
|
//
|
|
LaunchEditorOnFile( strTempFile, edName );
|
|
|
|
//
|
|
// read report back in from the temp file and remove any unchecked entries from
|
|
// the report
|
|
//
|
|
ReadTextReport( strTempFile );
|
|
|
|
//
|
|
// remove unchecked elements from remove
|
|
//
|
|
RemoveFCOsFromReport();
|
|
}
|
|
catch(...)
|
|
{
|
|
// delete the temp file, then rethrow
|
|
pFSServices->FileDelete( strTempFile );
|
|
throw;
|
|
}
|
|
//
|
|
// delete temp file
|
|
//
|
|
pFSServices->FileDelete( strTempFile );
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// PrintTextReport
|
|
//
|
|
// prints a text version of the passed in report to the passed in stream; the
|
|
// version that takes a file name is just a convenience function that calls the
|
|
// ostream version. '-' can be passed to signify stdout.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void cTextReportViewer::PrintTextReport( const TSTRING& strFilename, ReportingLevel level ) //throw (eTextReportViewer);
|
|
{
|
|
mReportingLevel = level;
|
|
|
|
if( strFilename.compare(_T("-")) == 0 )
|
|
{
|
|
mpOut = &TCOUT;
|
|
OutputTextReport();
|
|
}
|
|
else
|
|
{
|
|
fixed_basic_ofstream<TCHAR> out;
|
|
OpenOutputFile( out, strFilename );
|
|
mpOut = &out;
|
|
OutputTextReport();
|
|
out.close();
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// PrintTextReport
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void cTextReportViewer::PrintTextReport( TOSTREAM& ostr, ReportingLevel level )
|
|
{
|
|
mReportingLevel = level;
|
|
mpOut = &ostr;
|
|
OutputTextReport();
|
|
}
|
|
|
|
|
|
void cTextReportViewer::GetReportNumbers()
|
|
{
|
|
cFCOReportGenreIter genreIter( *mpReport );
|
|
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
|
|
{
|
|
// We need to at least get the max severity in this function
|
|
cFCOReportSpecIter ri( genreIter );
|
|
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
|
|
{
|
|
if( !IgnoreThisSpec( ri.GetAttr() ) )
|
|
{
|
|
int nAdded = ri.GetAddedSet()->Size();
|
|
int nRemoved = ri.GetRemovedSet()->Size();
|
|
int nChanged = ri.GetNumChanged();
|
|
|
|
if( nAdded || nRemoved || nChanged )
|
|
{
|
|
// update the max severity
|
|
if( mMaxSeverityViolated < ri.GetAttr()->GetSeverity() )
|
|
mMaxSeverityViolated = ri.GetAttr()->GetSeverity();
|
|
|
|
mNumberViolations += ( nAdded + nRemoved + nChanged );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mfGotNumbers = true;
|
|
}
|
|
|
|
void cTextReportViewer::OutputTextReport() //throw (eTextReportViewer);
|
|
{
|
|
ASSERT( mpOut != 0 );
|
|
|
|
InitOStream();
|
|
|
|
//
|
|
// count up total violations, max severity, etc.
|
|
//
|
|
GetReportNumbers();
|
|
|
|
//
|
|
// TODO: move this
|
|
//
|
|
if( SINGLE_LINE == mReportingLevel )
|
|
{
|
|
(*mpOut) << SingleLineReport() << endl;
|
|
}
|
|
else if( PARSEABLE == mReportingLevel )
|
|
{
|
|
OutputParseableReport();
|
|
}
|
|
else if( SUMMARY_ONLY <= mReportingLevel )
|
|
{
|
|
//
|
|
// output header, just like it sez....
|
|
//
|
|
OutputReportHeader();
|
|
|
|
//
|
|
// output summary info
|
|
//
|
|
OutputRulesSummary();
|
|
|
|
//
|
|
// print names of rule-breakers
|
|
//
|
|
// don't print out object summary if in CONCISE_REPORT because
|
|
// the detailed report will look a lot like the summary.
|
|
//
|
|
if( CONCISE_REPORT != mReportingLevel )
|
|
{
|
|
OutputObjectSummary();
|
|
}
|
|
|
|
if( CONCISE_REPORT <= mReportingLevel )
|
|
{
|
|
//
|
|
// print details of rule-breakers
|
|
//
|
|
OutputObjectDetails();
|
|
}
|
|
|
|
//
|
|
// output any errors
|
|
//
|
|
PrintErrors();
|
|
|
|
//
|
|
// output footer
|
|
//
|
|
OutputFooter();
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::SetUpForNewGenre( const cFCOReportGenreIter& genreIter )
|
|
{
|
|
//
|
|
// set up prop displayer and name translator
|
|
//
|
|
mpCurPD = genreIter.GetGenreHeader().GetPropDisplayer();
|
|
mpCurNT = cGenreSwitcher::GetInstance()->GetFactoryForGenre( genreIter.GetGenre() )->GetNameTranslator();
|
|
}
|
|
|
|
void cTextReportViewer::PrintErrors()
|
|
{
|
|
//
|
|
// output header
|
|
//
|
|
OutputSectionDelimiter( tw::STR_ERROR_REPORT );
|
|
|
|
//
|
|
// output any errors from the report's general error queue
|
|
//
|
|
bool fAtLeastOneGeneralError = false;
|
|
cErrorQueueIter eqIter( *( mpReport->GetErrorQueue() ) );
|
|
for( eqIter.SeekBegin(); !eqIter.Done(); eqIter.Next() )
|
|
{
|
|
ReportError( eqIter );
|
|
fAtLeastOneGeneralError = true;
|
|
}
|
|
|
|
// separate general errors from genre errors
|
|
if( fAtLeastOneGeneralError )
|
|
(*mpOut) << endl;
|
|
|
|
//
|
|
// cycle through genres
|
|
//
|
|
cFCOReportGenreIter genreIter( *mpReport );
|
|
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
|
|
{
|
|
//
|
|
// set up prop displayer and name translator
|
|
//
|
|
SetUpForNewGenre( genreIter );
|
|
|
|
//
|
|
// report errors for genre
|
|
//
|
|
bool fFirstErrorInGenre = true;
|
|
cFCOReportSpecIter rsi( genreIter );
|
|
for( rsi.SeekBegin(); !rsi.Done(); rsi.Next() )
|
|
{
|
|
cErrorQueueIter eqIter2( *( rsi.GetErrorQueue() ) );
|
|
for( eqIter2.SeekBegin(); !eqIter2.Done(); eqIter2.Next() )
|
|
{
|
|
if( fFirstErrorInGenre )
|
|
{
|
|
OutputGenreDelimiter( genreIter.GetGenre() );
|
|
fFirstErrorInGenre = false;
|
|
}
|
|
|
|
ReportError( eqIter2 );
|
|
}
|
|
}
|
|
|
|
// separate genre errors
|
|
if( ! fFirstErrorInGenre )
|
|
(*mpOut) << endl;
|
|
}
|
|
|
|
//
|
|
// if there were no errors, tell user
|
|
//
|
|
if( 1 == mErrorNum )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_REPORT_NO_ERRORS ) << endl << endl;
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputSectionDelimiter( int nString )
|
|
{
|
|
(*mpOut) << g_sz79Equals << endl;
|
|
(*mpOut) << TSS_GetString( cTW, nString ) << endl;
|
|
(*mpOut) << g_sz79Equals << endl << endl;
|
|
}
|
|
|
|
void cTextReportViewer::OutputGenreDelimiter( cGenre::Genre g, bool fDenoteBallotSection )
|
|
{
|
|
(*mpOut) << g_sz79Dashes << endl;
|
|
|
|
// a # denotes the ballot section
|
|
if( fDenoteBallotSection )
|
|
(*mpOut) << _T("# ");
|
|
else
|
|
(*mpOut) << _T(" ");
|
|
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_SECTION );
|
|
(*mpOut) << _T(": ") << cGenreSwitcher::GetInstance()->GenreToString( g, true ) << endl;
|
|
(*mpOut) << g_sz79Dashes << endl << endl;
|
|
}
|
|
|
|
void cTextReportViewer::OutputNumDetails( int nString, int nObjects )
|
|
{
|
|
(*mpOut) << _T(" ") << g_sz40Dashes << endl;
|
|
(*mpOut) << _T(" ") << TSS_GetString( cTW, nString ) << nObjects << endl;
|
|
(*mpOut) << _T(" ") << g_sz40Dashes << endl << endl;
|
|
}
|
|
|
|
void cTextReportViewer::OutputObjectSummary()
|
|
{
|
|
//
|
|
// check if we should output summary
|
|
//
|
|
if (!WantOutputObjectSummary())
|
|
return;
|
|
|
|
//
|
|
// output summary header
|
|
//
|
|
OutputSectionDelimiter( tw::STR_OBJECT_SUMMARY );
|
|
|
|
//
|
|
// cycle through genres
|
|
//
|
|
cFCOReportGenreIter genreIter( *mpReport );
|
|
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
|
|
{
|
|
//
|
|
// set up prop displayer and name translator
|
|
//
|
|
SetUpForNewGenre( genreIter );
|
|
|
|
//
|
|
// output genre delimiter
|
|
//
|
|
OutputGenreDelimiter( genreIter.GetGenre(), true );
|
|
|
|
//
|
|
// output object summary
|
|
//
|
|
OutputObjectSummary( genreIter );
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputObjectSummary( const cFCOReportGenreIter& genreIter )
|
|
{
|
|
// iterate over all report elements (keyed by a spec), and save
|
|
// references to all added, removed, and changed FCOs. We do this
|
|
// because we want to group the FCOs by added, removed, and changed,
|
|
// not by spec.
|
|
|
|
|
|
//
|
|
// if we're updating, we need to save a list of names
|
|
//
|
|
FCOList* pFCONameList = NULL;
|
|
if( mfUpdate )
|
|
pFCONameList = new FCOList;
|
|
|
|
cFCOReportSpecIter ri( genreIter );
|
|
bool violationReported = false;
|
|
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
|
|
{
|
|
// skip this spec if it's not one we care about for this report
|
|
if (IgnoreThisSpec(ri.GetAttr()))
|
|
continue;
|
|
|
|
//
|
|
// output a spec header if it was violated
|
|
//
|
|
if( ! SpecEmpty( ri ) )
|
|
{
|
|
violationReported = true;
|
|
|
|
OutputSpecHeader( ri );
|
|
(*mpOut) << endl;
|
|
|
|
if( mfUpdate )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_ADD_X ) << endl;
|
|
(*mpOut) << endl;
|
|
}
|
|
|
|
//
|
|
// output added set
|
|
//
|
|
OutputAddedSummary( ri, pFCONameList );
|
|
|
|
//
|
|
// output removed set
|
|
//
|
|
OutputRemovedSummary( ri, pFCONameList );
|
|
|
|
//
|
|
// output changed set
|
|
//
|
|
OutputChangedSummary( ri, pFCONameList );
|
|
}
|
|
}
|
|
|
|
if (violationReported == false)
|
|
{
|
|
// There are no violations for this section
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_NO_VIOLATIONS_IN_SECTION ) << endl << endl;
|
|
}
|
|
|
|
//
|
|
// insert FCO list into master list
|
|
//
|
|
if( mfUpdate )
|
|
mFCOsRemoveFromReport.insert( cTextReportViewer::GenreList::value_type( genreIter.GetGenre(), pFCONameList ) );
|
|
}
|
|
|
|
void cTextReportViewer::OutputObjectDetails()
|
|
{
|
|
//
|
|
// check if we should output details
|
|
//
|
|
if (!WantOutputObjectDetails())
|
|
return;
|
|
|
|
//
|
|
// output detail header
|
|
//
|
|
OutputSectionDelimiter( tw::STR_OBJECT_DETAIL );
|
|
|
|
//
|
|
// cycle through genres
|
|
//
|
|
cFCOReportGenreIter genreIter( *mpReport );
|
|
for(genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
|
|
{
|
|
//
|
|
// set up prop displayer and name translator
|
|
//
|
|
SetUpForNewGenre( genreIter );
|
|
|
|
//
|
|
// output genre delimiter
|
|
//
|
|
OutputGenreDelimiter( genreIter.GetGenre() );
|
|
|
|
//
|
|
// output details
|
|
//
|
|
OutputObjectDetails( genreIter );
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputObjectDetails( const cFCOReportGenreIter& genreIter )
|
|
{
|
|
//
|
|
// Now enumerate all changes
|
|
//
|
|
cFCOReportSpecIter ri( genreIter );
|
|
bool violationReported = false;
|
|
|
|
for(ri.SeekBegin() ; !ri.Done(); ri.Next() )
|
|
{
|
|
// skip this spec if it's not one we care about for this report
|
|
if (IgnoreThisSpec(ri.GetAttr()))
|
|
continue;
|
|
|
|
//
|
|
// output spec section header if there are changes
|
|
//
|
|
if( ! SpecEmpty( ri ) )
|
|
{
|
|
violationReported = true;
|
|
|
|
OutputSpecHeader( ri );
|
|
|
|
//
|
|
// output added set
|
|
//
|
|
OutputAddedDetails( ri );
|
|
|
|
//
|
|
// output removed set
|
|
//
|
|
OutputRemovedDetails( ri );
|
|
|
|
//
|
|
// output changed set
|
|
//
|
|
OutputChangedDetails( ri );
|
|
}
|
|
} // enum change details
|
|
|
|
if (violationReported == false)
|
|
{
|
|
// There are no violations for this section
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_NO_VIOLATIONS_IN_SECTION ) << endl << endl;
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputFooter()
|
|
{
|
|
(*mpOut) << g_sz79Dashes << endl;
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_END_OF_REPORT ) << endl << endl;
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_COPYRIGHT ) << endl;
|
|
}
|
|
|
|
|
|
bool cTextReportViewer::LaunchEditorOnFile( const TSTRING& strFilename, const TSTRING& edName ) //throw (eTextReportViewer)
|
|
{
|
|
bool fRanViewer = false;
|
|
TSTRING editor = edName;
|
|
|
|
// make sure we can read from this file
|
|
cFileUtil::TestFileReadable( strFilename );
|
|
|
|
// editor is going to need terminal type, so tell msystem to include
|
|
// it in environment when it makes its system call.
|
|
le_set("TERM");
|
|
le_set("DISPLAY"); // DISPLAY and HOME needed so we can launch X apps. X apps apparently check
|
|
le_set("HOME"); // a .xpermissions file in the users home dir
|
|
le_set("LANG"); // LANG allowed through in case any apps need
|
|
int systemRet = msystem( (char*) ( ( editor+ _T(' ') + strFilename ).c_str() ) );
|
|
le_unset("LANG");
|
|
le_unset("HOME");
|
|
le_unset("DISPLAY");
|
|
le_unset("TERM");
|
|
|
|
if( 0 == systemRet )
|
|
{
|
|
fRanViewer = true;
|
|
}
|
|
else
|
|
{
|
|
//ASSERT( false );
|
|
throw eTextReportViewerEditorLaunch( edName );
|
|
}
|
|
|
|
return( fRanViewer );
|
|
}
|
|
|
|
|
|
void cTextReportViewer::ReportError( const cErrorQueueIter& eqIter )
|
|
{
|
|
const int nWidth = 5;
|
|
|
|
// output error number
|
|
TOSTRINGSTREAM ostr;
|
|
ostr << mErrorNum << _T(".");
|
|
(*mpOut).width( nWidth );
|
|
(*mpOut) << ostr.str();
|
|
|
|
// output general error
|
|
(*mpOut) << cErrorTable::GetInstance()->Get( eqIter.GetError().GetID() )<< endl;
|
|
|
|
// output specific error
|
|
(*mpOut).width( nWidth );
|
|
(*mpOut) << _T(""); // this is here so the next output is 10 chars over
|
|
(*mpOut) << cDisplayUtil::FormatMultiLineString(
|
|
cDisplayEncoder::EncodeInlineAllowWS(
|
|
eqIter.GetError().GetMsg() ), nWidth, 0 ) << endl;
|
|
|
|
|
|
mErrorNum++;
|
|
}
|
|
|
|
void cTextReportViewer::PrintBallotLine( const iFCO& FCO )
|
|
{
|
|
ASSERT( mpOut != 0 );
|
|
|
|
//
|
|
// output "[X] <name of FCO>"
|
|
//
|
|
|
|
if( mfUpdate ) //only have Xs for updating
|
|
{
|
|
(*mpOut) << _T("[x] "); // TODO: should this be in resources?
|
|
}
|
|
|
|
TSTRING strEncoded = mpCurNT->ToStringDisplay( FCO.GetName(), true );
|
|
(*mpOut) << strEncoded.c_str();
|
|
|
|
(*mpOut) << endl;
|
|
}
|
|
|
|
|
|
// looks for "\n[" and leaves the file pointer at the next character
|
|
// could be a problem if the ballot box is the first line in the file, then "\n["
|
|
// wouldn't be encountered, only "[", so make sure that the ballots aren't
|
|
// on the first line
|
|
// returns false if finds "\n#" -- start of new genre
|
|
int cTextReportViewer::FindNextLineToken()
|
|
{
|
|
// loop over chars until (1) EOF is hit, or (2) we find "\n[" or '\n#'
|
|
for( GetChar(); !mpIn->eof(); GetChar() )
|
|
{
|
|
if( mCurrentChar[0] == '\n' )
|
|
{
|
|
// we'll only compare to single byte chars
|
|
// so it's ok to use PeekChar() instead of our GetChar()
|
|
if( PeekChar() == '[' )
|
|
{
|
|
return TOKEN_BALLOT_BOX;
|
|
}
|
|
else if( PeekChar() == '#' ) // start of new genre
|
|
{
|
|
return TOKEN_GENRE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return( TOKEN_EOF );
|
|
}
|
|
|
|
void cTextReportViewer::ReadTextReport( const TSTRING& strFilename ) //throw (eTextReportViewer)
|
|
{
|
|
std::ifstream in;
|
|
OpenInputFile( in, strFilename );
|
|
mpIn = ∈
|
|
|
|
//
|
|
// for each genre and ballot box in text report file
|
|
//
|
|
bool fMoreTokens = true;
|
|
FCOList* pCurList = NULL;
|
|
while( fMoreTokens )
|
|
{
|
|
switch( FindNextLineToken() )
|
|
{
|
|
case TOKEN_GENRE:
|
|
GetGenreInfo( &pCurList );
|
|
break;
|
|
case TOKEN_BALLOT_BOX:
|
|
GetBallotInfo( pCurList );
|
|
break;
|
|
case TOKEN_EOF:
|
|
fMoreTokens = false;
|
|
break;
|
|
default:
|
|
ASSERT( false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// close the file
|
|
//
|
|
in.close();
|
|
}
|
|
|
|
|
|
void cTextReportViewer::GetGenreInfo( FCOList** ppCurList )
|
|
{
|
|
//
|
|
// identify genre
|
|
//
|
|
TSTRING strGenre = GetGenre();
|
|
ASSERT( ! strGenre.empty() );
|
|
cGenre::Genre g = cGenreSwitcher::GetInstance()->StringToGenre( strGenre.c_str() );
|
|
if( cGenre::GENRE_INVALID == g)
|
|
{
|
|
throw eTextReportViewerReportCorrupt("Invalid Genre"); // TODO: ERR_UKNOWN_GENRE
|
|
}
|
|
cGenreSwitcher::GetInstance()->SelectGenre( g );
|
|
|
|
//
|
|
// get list of fcos in report for this genre
|
|
//
|
|
GenreList::iterator curIter = mFCOsRemoveFromReport.find( g );
|
|
if( curIter == mFCOsRemoveFromReport.end() )
|
|
throw eTextReportViewerReportCorrupt("No files found in report"); // TODO: ERR_UKNOWN_GENRE
|
|
*ppCurList = curIter->second;
|
|
|
|
//
|
|
// get the prop displayer
|
|
//
|
|
mpCurPD = cGenreSwitcher::GetInstance()->GetFactoryForGenre( g )->CreatePropDisplayer();
|
|
mpCurNT = cGenreSwitcher::GetInstance()->GetFactoryForGenre( g )->GetNameTranslator();
|
|
}
|
|
|
|
void cTextReportViewer::GetBallotInfo( FCOList* pCurList )
|
|
{
|
|
if( ! pCurList )
|
|
throw eTextReportViewerReportCorrupt("No ballot list found");
|
|
|
|
// if the box is checked, then the user elected to leave the item
|
|
// in the report, so we do nothing. If the box isn't checked, then
|
|
// we must remove the item from the report.
|
|
if( IsChecked() )
|
|
{
|
|
//
|
|
// look at the ballot line and get the name of the FCO
|
|
//
|
|
cFCOName fcoName;
|
|
GetFCONameFromBallotLine( fcoName );
|
|
|
|
///////////////////////////////////////
|
|
// remove this FCO from list
|
|
cTextReportViewer::FCOList::const_iterator iter;
|
|
iter = pCurList->find( fcoName );
|
|
if( iter == pCurList->end() )
|
|
{
|
|
throw eTextReportViewerReportCorrupt("Unknown file name in report");// TODO: ERR_UKNOWN_NAME
|
|
}
|
|
|
|
pCurList->erase( fcoName );
|
|
///////////////////////////////////////
|
|
}
|
|
}
|
|
|
|
// reads input up to EOL or EOF
|
|
TSTRING cTextReportViewer::GetGenre()
|
|
{
|
|
std::string strGenre;
|
|
|
|
EatSection();
|
|
|
|
while( true )
|
|
{
|
|
char chFirst = PeekChar();
|
|
if( PeekIsEOF() ||
|
|
chFirst == '\r' ||
|
|
chFirst == '\n' )
|
|
break;
|
|
|
|
// eat up char
|
|
GetChar();
|
|
AppendChar( strGenre );
|
|
}
|
|
|
|
return cStringUtil::StrToTstr( strGenre );
|
|
}
|
|
|
|
|
|
bool cTextReportViewer::PeekIsEOF()
|
|
{
|
|
return( mpIn->peek() == char_traits<char>::eof() );
|
|
}
|
|
|
|
// if the next character in the stream is ('X' or 'x'), it eats the x, else it returns false
|
|
// if the next character after the x is ']', it eats that, else it returns false.
|
|
bool cTextReportViewer::IsChecked()
|
|
{
|
|
bool fIsChecked = false;
|
|
char chFirst;
|
|
|
|
GetChar(); // eat up '['
|
|
chFirst = PeekChar(); // look for 'X'
|
|
|
|
if( ( ! PeekIsEOF() ) &&
|
|
( chFirst == 'X' || chFirst == 'x' ) ) // TODO: X in resources?
|
|
{
|
|
mpIn->get( chFirst ); // eat up X
|
|
|
|
char chSecond = PeekChar();
|
|
if( ( ! PeekIsEOF() ) &&
|
|
( chSecond == ']' ) )
|
|
{
|
|
fIsChecked = true;
|
|
mpIn->get( chSecond ); // eat up ']'
|
|
}
|
|
}
|
|
|
|
return( fIsChecked );
|
|
}
|
|
|
|
// FCO names are '\"' delimited and begin after the first '\"' after the ballot box
|
|
// NOTE: will not eat up a return character
|
|
void cTextReportViewer::GetFCONameFromBallotLine( cFCOName& fcoName ) //throw (eTextReportViewer)
|
|
{
|
|
char chIn;
|
|
std::string strFCOName;
|
|
|
|
#define TW_IS_QUOTE( ch ) ( ch == '\"' )
|
|
#define TW_IS_EOL( ch ) ( ch == '\n' || ch == '\r' )
|
|
#define TW_IS_BACKSLASH( ch ) ( ch == '\\' )
|
|
|
|
//
|
|
// go past any character that's not '\"' or EOL
|
|
// if we hit EOL, then there was no fconame present, so error
|
|
//
|
|
for( chIn = PeekChar();
|
|
! PeekIsEOF();
|
|
chIn = PeekChar() ) // while not EOF
|
|
{
|
|
if( TW_IS_QUOTE( chIn ) )
|
|
{
|
|
mpIn->get( chIn ); // eat up all chars but EOL
|
|
strFCOName += chIn; // add quote to string
|
|
|
|
// we've found our quote, break and get name
|
|
break;
|
|
}
|
|
else if( TW_IS_EOL( chIn ) )
|
|
{
|
|
// if EOL, there was no name!
|
|
throw eTextReportViewerReportCorrupt("Ballot item without a name");
|
|
}
|
|
else
|
|
{
|
|
GetChar(); // eat up all chars but EOL
|
|
}
|
|
}
|
|
|
|
//
|
|
// get FCO name (chars up until the next '<EOL>')
|
|
//
|
|
for( chIn = PeekChar();
|
|
! PeekIsEOF();
|
|
chIn = PeekChar() ) // while not EOF
|
|
{
|
|
if( TW_IS_EOL( chIn ) ) // if EOL, there was no ending quote!
|
|
{
|
|
// found end of fconame
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// add char to fco name
|
|
GetChar();
|
|
AppendChar( strFCOName );
|
|
}
|
|
}
|
|
|
|
// make sure we've got a name
|
|
if( strFCOName.empty()
|
|
||
|
|
! mpCurNT->DisplayStringToFCOName(
|
|
cStringUtil::StrToTstr( strFCOName ),
|
|
fcoName ) )
|
|
{
|
|
std::string msg = "Invalid object name: " + strFCOName;
|
|
throw eTextReportViewerReportCorrupt(msg); // TODO -- it might be nice to be able to specify what line of the report got corrupted
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::RemoveFCOsFromReport() //throw (eTextReportViewer)
|
|
{
|
|
// TODO: remove fconames from fcosToRemove when we find them in report,
|
|
// so we don't compare that fconame again. Watch out for messing
|
|
// with iters in the for, loops, though! That's why I didn't do
|
|
// it yet... (and I figure most users would only un-X a few items,
|
|
// in which case it is currently faster anyways...)
|
|
|
|
|
|
//
|
|
// iterate over all specs in report, then in each of the added,
|
|
// removed, and changed sets, lookup the FCOName and, if found,
|
|
// delete it
|
|
//
|
|
int nFCOsRemoved = 0;
|
|
|
|
cFCOReportGenreIter rgi( *mpReport );
|
|
for( rgi.SeekBegin(); !rgi.Done(); rgi.Next() )
|
|
{
|
|
cTextReportViewer::GenreList::iterator currentList = mFCOsRemoveFromReport.find( rgi.GetGenre() );
|
|
ASSERT( currentList != mFCOsRemoveFromReport.end() );
|
|
cTextReportViewer::FCOList* pFCOList = currentList->second;
|
|
|
|
cFCOReportSpecIter rsi( rgi );
|
|
for( rsi.SeekBegin(); !rsi.Done(); rsi.Next() )
|
|
{
|
|
// search added
|
|
const iFCOSet* pAddedSet = rsi.GetAddedSet();
|
|
ASSERT( pAddedSet != 0 );
|
|
|
|
const iFCOIter* fcoIter = NULL;
|
|
cTextReportViewer::FCOList::iterator iter;
|
|
//
|
|
// see if any FCOs to remove are in this set
|
|
//
|
|
for( iter = pFCOList->begin(); iter != pFCOList->end(); ++iter )
|
|
{
|
|
fcoIter = pAddedSet->Lookup( *iter );
|
|
if( fcoIter )
|
|
{
|
|
fcoIter->Remove();
|
|
nFCOsRemoved++;
|
|
|
|
fcoIter->DestroyIter();
|
|
}
|
|
}
|
|
|
|
// search removed
|
|
// get removed set
|
|
const iFCOSet* pRemovedSet = rsi.GetRemovedSet();
|
|
ASSERT( pRemovedSet != 0 );
|
|
|
|
//
|
|
// see if any FCOs to remove are in this set
|
|
//
|
|
for( iter = pFCOList->begin(); iter != pFCOList->end(); ++iter )
|
|
{
|
|
fcoIter = pRemovedSet->Lookup( *iter );
|
|
if( fcoIter )
|
|
{
|
|
fcoIter->Remove();
|
|
nFCOsRemoved++;
|
|
|
|
fcoIter->DestroyIter();
|
|
}
|
|
}
|
|
|
|
|
|
for( iter = pFCOList->begin(); iter != pFCOList->end(); ++iter )
|
|
{
|
|
// search changed
|
|
// get changed set iterator
|
|
cFCOReportChangeIter changedIter( rsi );
|
|
|
|
// iterate over all changed fcos
|
|
for( changedIter.SeekBegin(); !changedIter.Done(); changedIter.Next() )
|
|
{
|
|
cFCOName name = changedIter.GetOld()->GetName();
|
|
|
|
if( name.IsEqual( *iter ) )
|
|
{
|
|
changedIter.Remove();
|
|
nFCOsRemoved++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// count FCOs to remove
|
|
//
|
|
int nFCOsToRemove = 0;
|
|
cTextReportViewer::GenreList::const_iterator iter = mFCOsRemoveFromReport.begin();
|
|
for( ; iter != mFCOsRemoveFromReport.end(); ++iter )
|
|
nFCOsToRemove += iter->second->size();
|
|
|
|
if( nFCOsToRemove != nFCOsRemoved )
|
|
{
|
|
// TODO -- maybe have a different enumeration for this?
|
|
throw eTextReportViewerReportCorrupt("Mismatch in objects to remove");
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputReportHeader()
|
|
{
|
|
if (!WantOutputReportHeader())
|
|
return;
|
|
|
|
const int headerColumnWidth = 30;
|
|
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_REPORT_TITLE ) << endl << endl;
|
|
|
|
|
|
(*mpOut) << setw(headerColumnWidth)
|
|
<< TSS_GetString( cTW, tw::STR_R_GENERATED_BY )
|
|
<< util_Encode( mpHeader->GetCreator() )
|
|
<< endl;
|
|
|
|
// TODO: ( start / end / elapsed ) time
|
|
TSTRING tstrDummy;
|
|
int64 i64CreateTime = mpHeader->GetCreationTime();
|
|
(*mpOut).width(headerColumnWidth);
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_R_CREATED_ON ) << cTWLocale::FormatTime( i64CreateTime, tstrDummy ).c_str() << endl;
|
|
|
|
(*mpOut).width(headerColumnWidth);
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_DB_LAST_UPDATE );
|
|
|
|
int64 i64LastDBUTime = mpHeader->GetLastDBUpdateTime();
|
|
if( i64LastDBUTime == 0 )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_NEVER ) << endl << endl;
|
|
}
|
|
else
|
|
{
|
|
(*mpOut) << cTWLocale::FormatTime( i64LastDBUTime, tstrDummy ).c_str() << endl << endl;
|
|
}
|
|
|
|
OutputSectionDelimiter( tw::STR_R_SUMMARY );
|
|
|
|
(*mpOut).width(headerColumnWidth);
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_HOST_NAME ) << mpHeader->GetSystemName().c_str() << endl;
|
|
(*mpOut).width(headerColumnWidth);
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_HOST_IP ) << mpHeader->GetIPAddress() << endl;
|
|
|
|
(*mpOut).width(headerColumnWidth);
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_HOST_ID );
|
|
|
|
if(! mpHeader->GetHostID().empty() )
|
|
(*mpOut) << mpHeader->GetHostID() << endl;
|
|
else
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_NONE ) << endl;
|
|
|
|
(*mpOut) << setw(headerColumnWidth)
|
|
<< TSS_GetString( cTW, tw::STR_POLICY_FILE_USED )
|
|
<< util_Encode( mpHeader->GetPolicyFilename() )
|
|
<< endl;
|
|
|
|
(*mpOut) << setw(headerColumnWidth)
|
|
<< TSS_GetString( cTW, tw::STR_CONFIG_FILE_USED )
|
|
<< util_Encode( mpHeader->GetConfigFilename() )
|
|
<< endl;
|
|
|
|
(*mpOut) << setw(headerColumnWidth)
|
|
<< TSS_GetString( cTW, tw::STR_DB_FILE_USED )
|
|
<< util_Encode( mpHeader->GetDBFilename() )
|
|
<< endl;
|
|
|
|
(*mpOut) << setw(headerColumnWidth)
|
|
<< TSS_GetString( cTW, tw::STR_CMD_LINE_USED )
|
|
<< util_Encode( mpHeader->GetCommandLineParams() )
|
|
<< endl
|
|
<< endl;
|
|
}
|
|
|
|
void cTextReportViewer::OutputRulesSummary()
|
|
{
|
|
//
|
|
// check if we should output summary
|
|
//
|
|
if( !WantOutputRulesSummary() )
|
|
return;
|
|
|
|
//
|
|
// output summary header
|
|
//
|
|
OutputSectionDelimiter( tw::STR_RULE_SUMMARY );
|
|
|
|
//
|
|
// cycle through genres
|
|
//
|
|
cFCOReportGenreIter genreIter( *mpReport );
|
|
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
|
|
{
|
|
//
|
|
// set up prop displayer and name translator
|
|
//
|
|
SetUpForNewGenre( genreIter );
|
|
|
|
//
|
|
// output genre delimiter
|
|
//
|
|
OutputGenreDelimiter( genreIter.GetGenre() );
|
|
|
|
//
|
|
// output rules summary
|
|
//
|
|
OutputRulesSummary( genreIter );
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputRulesSummary( const cFCOReportGenreIter& genreIter )
|
|
{
|
|
// output spec stats header
|
|
// 2 + specNameWidth + severityWidth + 3*numObjectsWidth should be < 80
|
|
const int specNameWidth = 32;
|
|
const int severityWidth = 18;
|
|
const int numObjectsWidth = 9;
|
|
(*mpOut) << _T(" "); // indent
|
|
(*mpOut).width( specNameWidth );
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_RULE_NAME );
|
|
(*mpOut).width( severityWidth );
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_SEVERITY_LEVEL );
|
|
(*mpOut).width( numObjectsWidth );
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_ADDED );
|
|
(*mpOut).width( numObjectsWidth );
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_REMOVED );
|
|
(*mpOut).width( numObjectsWidth );
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_CHANGED );
|
|
(*mpOut) << endl;
|
|
|
|
(*mpOut) << _T(" ");
|
|
(*mpOut) << setw( specNameWidth ) << _T("---------");
|
|
(*mpOut) << setw( severityWidth ) << _T("--------------");
|
|
(*mpOut) << setw( numObjectsWidth ) << _T("-----");
|
|
(*mpOut) << setw( numObjectsWidth ) << _T("-------");
|
|
(*mpOut) << setw( numObjectsWidth ) << _T("--------");
|
|
(*mpOut) << endl;
|
|
|
|
// merge rules of duplicate names and severities
|
|
ObjectSummaryList summaryList;
|
|
CollateRulesSummary( genreIter, summaryList );
|
|
|
|
// Output the information in the list
|
|
int nTotalObjectsChanged = 0;
|
|
ObjectSummaryList::iterator si;
|
|
for (si = summaryList.begin(); si != summaryList.end(); ++si)
|
|
{
|
|
// highlight violated rules
|
|
if( si->mAddedObjects + si->mRemovedObjects + si->mChangedObjects > 0 )
|
|
(*mpOut) << _T("* ");
|
|
else
|
|
(*mpOut) << _T(" "); // indent
|
|
|
|
TSTRING strSpecName = util_Encode( si->mSpecName );
|
|
(*mpOut) << setw( specNameWidth )
|
|
<< strSpecName;
|
|
|
|
// if the name is too long, put it on its own line
|
|
if( strSpecName.length() >= (unsigned int)specNameWidth )
|
|
{
|
|
(*mpOut) << endl;
|
|
(*mpOut) << _T(" "); // indent
|
|
(*mpOut).width( specNameWidth );
|
|
(*mpOut) << _T(""); // output space holder
|
|
}
|
|
|
|
(*mpOut).width( severityWidth );
|
|
(*mpOut) << si->mSeverity;
|
|
(*mpOut).width( numObjectsWidth );
|
|
(*mpOut) << si->mAddedObjects;
|
|
(*mpOut).width( numObjectsWidth );
|
|
(*mpOut) << si->mRemovedObjects;
|
|
(*mpOut).width( numObjectsWidth );
|
|
(*mpOut) << si->mChangedObjects;
|
|
(*mpOut) << endl;
|
|
|
|
// output start point on next line
|
|
// TODO: I am not sure if I like this in the new collated reports, dmb Sept 16 1999
|
|
if (!si->mStartPoint.empty())
|
|
(*mpOut) << _T(" (") << si->mStartPoint << _T(")") << endl;
|
|
|
|
nTotalObjectsChanged += si->mAddedObjects + si->mRemovedObjects + si->mChangedObjects;
|
|
}
|
|
|
|
(*mpOut) << endl;
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_OBJECTS_SCANNED ) << _T(" ") << genreIter.GetGenreHeader().GetObjectsScanned() << endl;
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_TOTAL_VIOLATIONS ) << _T(" ") << nTotalObjectsChanged << endl;
|
|
(*mpOut) << endl;
|
|
}
|
|
|
|
|
|
void cTextReportViewer::CollateRulesSummary( const cFCOReportGenreIter& genreIter, ObjectSummaryList& summaryList )
|
|
{
|
|
summaryList.clear();
|
|
|
|
cFCOReportSpecIter ri( genreIter );
|
|
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
|
|
{
|
|
// skip this spec if it's not one we care about for this report
|
|
if (IgnoreThisSpec(ri.GetAttr()))
|
|
continue;
|
|
|
|
RuleSummaryLine newLine;
|
|
|
|
newLine.mSpecName = util_Encode( ri.GetSpec()->GetName() );
|
|
newLine.mSeverity = ri.GetAttr()->GetSeverity();
|
|
newLine.mAddedObjects = ri.GetAddedSet()->Size();
|
|
newLine.mRemovedObjects = ri.GetRemovedSet()->Size();
|
|
newLine.mChangedObjects = ri.GetNumChanged();
|
|
newLine.mStartPoint = mpCurNT->ToStringDisplay( ri.GetSpec()->GetStartPoint() );
|
|
|
|
// If there is already a summary line for this rule with equivalent severity, merge this one into it
|
|
ObjectSummaryList::iterator si;
|
|
for (si = summaryList.begin(); ; ++si)
|
|
{
|
|
if (si == summaryList.end())
|
|
{
|
|
summaryList.push_back(newLine);
|
|
break;
|
|
}
|
|
|
|
if (si->mSpecName.compare(newLine.mSpecName) == 0 &&
|
|
si->mSeverity == newLine.mSeverity)
|
|
{
|
|
si->mAddedObjects += newLine.mAddedObjects;
|
|
si->mRemovedObjects += newLine.mRemovedObjects;
|
|
si->mChangedObjects += newLine.mChangedObjects;
|
|
|
|
// if one of the start points is a subset of the other, then we take the shorter one.
|
|
// otherwise we set the startpoint to empty.
|
|
if (newLine.mStartPoint.length() <= si->mStartPoint.length())
|
|
if (newLine.mStartPoint.compare(0, newLine.mStartPoint.length(), si->mStartPoint) == 0)
|
|
si->mStartPoint = newLine.mStartPoint;
|
|
else
|
|
si->mStartPoint.erase();
|
|
else
|
|
if (si->mStartPoint.compare(0, si->mStartPoint.length(), newLine.mStartPoint) == 0)
|
|
;
|
|
else
|
|
si->mStartPoint.erase();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void cTextReportViewer::OutputSpecHeader( const cFCOReportSpecIter &ri )
|
|
{
|
|
if (!WantOutputSpecHeader())
|
|
return;
|
|
|
|
(*mpOut) << g_sz79Dashes << endl;
|
|
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_RULE_NAME ) << _T(": ") << util_Encode( ri.GetSpec()->GetName() );
|
|
(*mpOut) << _T( " (" ) << mpCurNT->ToStringDisplay( ri.GetSpec()->GetStartPoint() ) << _T( ")" ) << endl;
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_SEVERITY_LEVEL ) << _T(": ") << ri.GetAttr()->GetSeverity() << endl;
|
|
|
|
(*mpOut) << g_sz79Dashes << endl;
|
|
}
|
|
|
|
void cTextReportViewer::DisplayChangedProps( const iFCO* const pfcoOld, const iFCO* const pfcoNew, const cFCOPropVector* pv )
|
|
{
|
|
ASSERT( mpOut != 0 );
|
|
ASSERT( pfcoOld || pfcoNew); // have to have at least one to display
|
|
|
|
const iFCO* pfcoValid = pfcoOld ? pfcoOld : pfcoNew; // we need to use one of the pfco's for some Get functions
|
|
|
|
// marginWidth + attrNameWidth + ( 2 * attrValueWidth ) should be < 80
|
|
const int attrNameWidth = 21;
|
|
const int attrValueWidth = 28;
|
|
const int marginWidth = 2; // for " " or "* "
|
|
ASSERT( ( attrNameWidth + ( 2 * attrValueWidth ) + marginWidth ) < 80 );
|
|
|
|
// output header
|
|
(*mpOut) << setw( marginWidth ) << _T("");
|
|
(*mpOut) << setw( attrNameWidth ) << TSS_GetString( cTW, tw::STR_ATTRIBUTES );
|
|
(*mpOut) << setw( attrValueWidth ) << TSS_GetString( cTW, tw::STR_EXPECTED );
|
|
(*mpOut) << setw( attrValueWidth ) << TSS_GetString( cTW, tw::STR_OBSERVED );
|
|
(*mpOut) << endl;
|
|
|
|
(*mpOut) << setw( marginWidth ) << _T("");
|
|
(*mpOut) << setw( attrNameWidth ) << _T("-------------");
|
|
(*mpOut) << setw( attrValueWidth ) << _T("-----------");
|
|
(*mpOut) << setw( attrValueWidth ) << _T("-----------");
|
|
(*mpOut) << endl;
|
|
|
|
int iNumProps = pfcoValid->GetPropSet()->GetNumProps(); // TODO: what if new + old have different props
|
|
for( int j = 0; j < iNumProps; j++ )
|
|
{
|
|
// if not a full report, only print properties that have changed
|
|
if( FULL_REPORT != mReportingLevel )
|
|
{
|
|
if( !pv || !pv->ContainsItem( j ) )
|
|
continue;
|
|
}
|
|
|
|
// ignore certain combinations of undef and non-existent props
|
|
if( ! PrintableProp( pfcoOld, pfcoNew, j ) )
|
|
continue;
|
|
|
|
// highlight changes and set width for prop name
|
|
if( ! mpCurPD->IsMultiLineProp( j ) )
|
|
{
|
|
if( !pv || pv->ContainsItem( j ) )
|
|
(*mpOut) << setw( marginWidth ) << _T("*");
|
|
else
|
|
(*mpOut) << setw( marginWidth ) << _T("");
|
|
}
|
|
else // for multiline props we will highlight the observed prop if it has changed
|
|
{
|
|
(*mpOut) << setw( marginWidth ) << _T("");
|
|
}
|
|
|
|
|
|
//
|
|
// output prop name
|
|
//
|
|
{
|
|
TSTRING strNewName;
|
|
strNewName = pfcoValid->GetPropSet()->GetPropName(j);
|
|
|
|
if( mpCurPD->IsMultiLineProp( j ) )
|
|
{
|
|
strNewName += _T(" ") ;
|
|
strNewName += TSS_GetString( cTW, tw::STR_EXPECTED );
|
|
}
|
|
|
|
//strNewName += _T(':') ;
|
|
|
|
(*mpOut) << setw( attrNameWidth ) << strNewName;
|
|
}
|
|
|
|
//
|
|
// output old value
|
|
//
|
|
TSTRING strOldValue;
|
|
(*mpOut).width( attrValueWidth );
|
|
if( pfcoOld && pfcoOld->GetPropSet()->GetValidVector().ContainsItem( j ) )
|
|
strOldValue = mpCurPD->PropAsString( pfcoOld, j, ( marginWidth + attrNameWidth ), 0 ).c_str();
|
|
else
|
|
strOldValue = _T("---");
|
|
(*mpOut) << strOldValue;
|
|
|
|
|
|
//
|
|
// output name again, if necessary (if multiline prop)
|
|
//
|
|
if( mpCurPD->IsMultiLineProp( j ) )
|
|
{
|
|
TSTRING strNewName;
|
|
strNewName = pfcoValid->GetPropSet()->GetPropName(j);
|
|
strNewName += _T(" ") ;
|
|
strNewName += TSS_GetString( cTW, tw::STR_OBSERVED );
|
|
//strNewName += _T(':') ;
|
|
|
|
(*mpOut) << endl;
|
|
|
|
if( !pv || pv->ContainsItem( j ) )
|
|
(*mpOut) << setw( marginWidth ) << _T("*");
|
|
else
|
|
(*mpOut) << setw( marginWidth ) << _T("");
|
|
|
|
(*mpOut) << setw( attrNameWidth ) << strNewName;
|
|
}
|
|
else
|
|
{
|
|
// if old value would run into this value, we need to put this value
|
|
// on the next line (but in the same column). don't output
|
|
// value but position "cursor" in the correct place
|
|
if( strOldValue.length() >= (unsigned int)attrValueWidth )
|
|
{
|
|
(*mpOut) << endl;
|
|
(*mpOut) << setw( marginWidth ) << _T("");
|
|
(*mpOut) << setw( attrNameWidth ) << _T("");
|
|
(*mpOut) << setw( attrValueWidth ) << _T("");
|
|
}
|
|
}
|
|
|
|
//
|
|
// output new value
|
|
//
|
|
(*mpOut).width( attrValueWidth );
|
|
if( pfcoNew && pfcoNew->GetPropSet()->GetValidVector().ContainsItem( j ) )
|
|
(*mpOut) << mpCurPD->PropAsString( pfcoNew, j, ( marginWidth + attrNameWidth ), 0 ).c_str();
|
|
else
|
|
(*mpOut) << _T("---");
|
|
|
|
(*mpOut) << endl;
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputAddedSummary( const cFCOReportSpecIter& ri, FCOList* pFCONameList )
|
|
{
|
|
if( ! ri.GetAddedSet()->IsEmpty() )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_ADDED ) << _T(":") << endl;
|
|
|
|
// iterate over all fcos
|
|
const cIterProxy<iFCOIter> pSetIterAdded = ri.GetAddedSet()->GetIter();
|
|
for( pSetIterAdded->SeekBegin(); !pSetIterAdded->Done(); pSetIterAdded->Next() )
|
|
{
|
|
PrintBallotLine( *pSetIterAdded->FCO() );
|
|
|
|
// if we're updating, save a list of FCO names
|
|
if( mfUpdate && pFCONameList)
|
|
pFCONameList->insert( pSetIterAdded->FCO()->GetName() );
|
|
}
|
|
(*mpOut) << endl;
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputRemovedSummary( const cFCOReportSpecIter& ri, FCOList* pFCONameList)
|
|
{
|
|
if( ! ri.GetRemovedSet()->IsEmpty() )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_REMOVED ) << _T(":") << endl;
|
|
|
|
// iterate over all fcos
|
|
const cIterProxy<iFCOIter> pSetIterRemoved = ri.GetRemovedSet()->GetIter();
|
|
for( pSetIterRemoved->SeekBegin(); !pSetIterRemoved->Done(); pSetIterRemoved->Next() )
|
|
{
|
|
PrintBallotLine( *pSetIterRemoved->FCO() );
|
|
|
|
// if we're updating, save a list of FCO names
|
|
if( mfUpdate && pFCONameList)
|
|
pFCONameList->insert( pSetIterRemoved->FCO()->GetName() );
|
|
}
|
|
(*mpOut) << endl;
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputChangedSummary( const cFCOReportSpecIter& ri, FCOList* pFCONameList )
|
|
{
|
|
if( ri.GetNumChanged() > 0 )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_CHANGED ) << _T(":") << endl;
|
|
|
|
// iterate over all changed fcos
|
|
cFCOReportChangeIter changedIter( ri );
|
|
for( changedIter.SeekBegin(); !changedIter.Done(); changedIter.Next() )
|
|
{
|
|
PrintBallotLine( *changedIter.GetNew() );
|
|
|
|
// if we're updating, save a list of FCO names
|
|
if( mfUpdate && pFCONameList)
|
|
pFCONameList->insert( changedIter.GetNew()->GetName() );
|
|
}
|
|
|
|
(*mpOut) << endl;
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputAddedDetails( const cFCOReportSpecIter& ri )
|
|
{
|
|
if( ! ri.GetAddedSet()->IsEmpty() )
|
|
{
|
|
OutputNumDetails( tw::STR_ADDED_FILES, ri.GetAddedSet()->Size() );
|
|
|
|
// iterate over all fcos
|
|
const cIterProxy<iFCOIter> pSetIterAdded = ri.GetAddedSet()->GetIter(); ASSERT( pSetIterAdded != 0 );
|
|
for( pSetIterAdded->SeekBegin(); !pSetIterAdded->Done(); pSetIterAdded->Next() )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_ADDED_FILE_NAME ) << _T(" ") << mpCurNT->ToStringDisplay( pSetIterAdded->FCO()->GetName() ).c_str() << endl;
|
|
|
|
if( FULL_REPORT == mReportingLevel )
|
|
{
|
|
(*mpOut) << endl;
|
|
DisplayChangedProps( NULL, pSetIterAdded->FCO(), NULL );
|
|
(*mpOut) << endl << endl;
|
|
}
|
|
}
|
|
|
|
(*mpOut) << endl;
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputRemovedDetails( const cFCOReportSpecIter& ri )
|
|
{
|
|
if( ! ri.GetRemovedSet()->IsEmpty() )
|
|
{
|
|
OutputNumDetails( tw::STR_REMOVED_FILES, ri.GetRemovedSet()->Size() );
|
|
|
|
// iterate over all fcos
|
|
const cIterProxy<iFCOIter> pSetIterRemoved = ri.GetRemovedSet()->GetIter(); ASSERT( pSetIterRemoved != 0 );
|
|
for( pSetIterRemoved->SeekBegin(); !pSetIterRemoved->Done(); pSetIterRemoved->Next() )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_REMOVED_FILE_NAME ) << _T(" ") << mpCurNT->ToStringDisplay( pSetIterRemoved->FCO()->GetName() ).c_str() << endl;
|
|
|
|
if( FULL_REPORT == mReportingLevel )
|
|
{
|
|
(*mpOut) << endl;
|
|
DisplayChangedProps( pSetIterRemoved->FCO(), NULL, NULL );
|
|
(*mpOut) << endl << endl;
|
|
}
|
|
}
|
|
(*mpOut) << endl;
|
|
}
|
|
}
|
|
|
|
void cTextReportViewer::OutputChangedDetails( const cFCOReportSpecIter& ri )
|
|
{
|
|
if( ri.GetNumChanged() > 0 )
|
|
{
|
|
OutputNumDetails( tw::STR_CHANGED_FILES, ri.GetNumChanged() );
|
|
|
|
// iterate over all changed fcos
|
|
cFCOReportChangeIter changedIter( ri );
|
|
for( changedIter.SeekBegin(); !changedIter.Done(); changedIter.Next() )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_CHANGED_FILE_NAME ) << _T(" ") << mpCurNT->ToStringDisplay( changedIter.GetOld()->GetName() ).c_str() << endl << endl;
|
|
DisplayChangedProps( changedIter.GetOld(), changedIter.GetNew(), &changedIter.GetChangeVector() );
|
|
(*mpOut) << endl << endl;
|
|
}
|
|
(*mpOut) << endl;
|
|
}
|
|
}
|
|
|
|
// overridables:
|
|
// These function allows derived classes to tailor reports
|
|
bool cTextReportViewer::IgnoreThisSpec(const cFCOSpecAttr *attr) { return false; }
|
|
bool cTextReportViewer::WantOutputReportHeader() { return true; }
|
|
bool cTextReportViewer::WantOutputRulesSummary() { return true; }
|
|
bool cTextReportViewer::WantOutputSpecHeader() { return true; }
|
|
bool cTextReportViewer::WantOutputObjectSummary() { return true; }
|
|
bool cTextReportViewer::WantOutputObjectDetails() { return true; }
|
|
bool cTextReportViewer::CanUpdate() { return true; }
|
|
|
|
|
|
//=========================================================================
|
|
// UTIL FUNCTION CODE
|
|
//=========================================================================
|
|
|
|
void OpenInputFile( std::ifstream& in, const TSTRING& strFile ) // throw( eTextReportViewer )
|
|
{
|
|
in.open( cStringUtil::TstrToStr( strFile ).c_str() );
|
|
|
|
if(! in.is_open())
|
|
{
|
|
throw eTextReportViewerFileOpen( strFile );
|
|
}
|
|
}
|
|
|
|
void OpenOutputFile( fixed_basic_ofstream<TCHAR>& out, const TSTRING& strFile ) // throw( eTextReportViewer )
|
|
{
|
|
std::string narrowFilename = cStringUtil::TstrToStr( strFile );
|
|
|
|
// we explicitly create the file so that we control the
|
|
// permissions on the new file.
|
|
unlink(narrowFilename.c_str());
|
|
int fd = open(narrowFilename.c_str(), O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 00600);
|
|
if (fd == -1 || close(fd) != 0) {
|
|
throw eTextReportViewerFileOpen( strFile );
|
|
}
|
|
|
|
out.open( narrowFilename.c_str() );
|
|
|
|
if(! out.is_open())
|
|
throw eTextReportViewerFileOpen( strFile );
|
|
}
|
|
|
|
bool SpecEmpty( const cFCOReportSpecIter& ri )
|
|
{
|
|
return( ri.GetAddedSet()->IsEmpty() && ri.GetRemovedSet()->IsEmpty() && 0 == ri.GetNumChanged() );
|
|
}
|
|
|
|
bool PrintableProp( const iFCO* pfcoOld, const iFCO* pfcoNew, int j )
|
|
{
|
|
bool fOldExists = pfcoOld && pfcoOld->GetPropSet()->GetValidVector().ContainsItem( j );
|
|
bool fNewExists = pfcoNew && pfcoNew->GetPropSet()->GetValidVector().ContainsItem( j );
|
|
|
|
//
|
|
// don't output line if neither FCO has the prop
|
|
//
|
|
if( ! fOldExists && ! fNewExists )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// don't output anything if both props are undefined
|
|
//
|
|
if ( fOldExists && fNewExists &&
|
|
pfcoOld->GetPropSet()->GetPropAt( j )->GetType()
|
|
== cFCOUndefinedProp::GetInstance()->GetType() &&
|
|
pfcoNew->GetPropSet()->GetPropAt( j )->GetType()
|
|
== cFCOUndefinedProp::GetInstance()->GetType() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// don't output anything if old is undef and new doesn't exist
|
|
//
|
|
if(
|
|
fOldExists
|
|
&&
|
|
! fNewExists
|
|
&&
|
|
pfcoOld->GetPropSet()->GetPropAt( j )->GetType() == cFCOUndefinedProp::GetInstance()->GetType()
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// don't output anything if new is undef and old doesn't exist
|
|
//
|
|
if(
|
|
! fOldExists
|
|
&&
|
|
fNewExists
|
|
&&
|
|
pfcoNew->GetPropSet()->GetPropAt( j )->GetType() == cFCOUndefinedProp::GetInstance()->GetType()
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void cTextReportViewer::EatSection()
|
|
{
|
|
GetChar(); // get '#'
|
|
|
|
// eat up " section: "
|
|
int nb =
|
|
cStringUtil::TstrToStr(
|
|
TSS_GetString( cTW, tw::STR_SECTION )
|
|
).length();
|
|
|
|
nb += 3; // one for each space surrounding section, and one for colon
|
|
|
|
// eat up nb bytes
|
|
for( int i = 0; !mpIn->eof() && i < nb; i++ )
|
|
mpIn->get();
|
|
}
|
|
|
|
//=========================================================================
|
|
//
|
|
// Methods of class cEmailReportViewer
|
|
//
|
|
//=========================================================================
|
|
|
|
cEmailReportViewer::cEmailReportViewer( const cFCOReportHeader& h, const cFCOReport& r,
|
|
const TSTRING &address, bool bForceFullReport /*= false */)
|
|
: cTextReportViewer( h, r ), mbForceFullReport(bForceFullReport)
|
|
{
|
|
mAddress = address;
|
|
}
|
|
|
|
bool cEmailReportViewer::IgnoreThisSpec(const cFCOSpecAttr *attr)
|
|
{
|
|
// IgnoreThisSpec - returns false if the email address is specified
|
|
// for the given spec attributes.
|
|
|
|
if (mbForceFullReport)
|
|
return false;
|
|
|
|
// loop through all email addresses for this spec
|
|
cFCOSpecAttrEmailIter emailIter(*attr);
|
|
for(emailIter.SeekBegin(); ! emailIter.Done(); emailIter.Next())
|
|
{
|
|
if (emailIter.EmailAddress() == mAddress)
|
|
return false; // Don't ignore it. It applies to me.
|
|
}
|
|
|
|
return true; // Ignore it. It doesn't have my name on it.
|
|
}
|
|
|
|
bool cEmailReportViewer::WantOutputObjectSummary()
|
|
{
|
|
// This area is not useful unless you're doing the ballot box thing,
|
|
// which is not applicable to emailed reports.
|
|
return false;
|
|
}
|
|
|
|
bool cEmailReportViewer::CanUpdate()
|
|
{
|
|
// this derived class does not allow "DisplayReportAndHaveUserUpdateIt()"
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
//=========================================================================
|
|
// 6/16/99 -- new reporting stuff
|
|
//=========================================================================
|
|
TSTRING cTextReportViewer::SingleLineReport()
|
|
{
|
|
TOSTRINGSTREAM sstrReport;
|
|
ASSERT( mpReport != 0 );
|
|
ASSERT( mpHeader != 0 );
|
|
|
|
//
|
|
// get current time
|
|
//
|
|
tm* ptm = cTimeUtil::TimeToDateLocal( mpHeader->GetCreationTime() );
|
|
if( NULL == ptm )
|
|
ThrowAndAssert( ePoly() );
|
|
|
|
//
|
|
// format time: YYYYMMDDHHMMSS
|
|
//
|
|
TCHAR szDate[256];
|
|
_tcsftime( szDate, countof( szDate ), _T("%Y%m%d%H%M%S"), ptm );
|
|
|
|
sstrReport << TSS_GetString( cTW, tw::STR_TRIPWIRE_REPORT_SHORT );
|
|
sstrReport << _T(" ");
|
|
sstrReport << mpHeader->GetSystemName();
|
|
sstrReport << _T(" ");
|
|
sstrReport << szDate;
|
|
|
|
//
|
|
// get report stats
|
|
//
|
|
int nAddedTotal = 0;
|
|
int nRemovedTotal = 0;
|
|
int nChangedTotal = 0;
|
|
int nViolations = 0;
|
|
int maxSeverityViolated = 0;
|
|
|
|
cFCOReportGenreIter genreIter( *mpReport );
|
|
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
|
|
{
|
|
// We need to at least get the max severity in this function
|
|
cFCOReportSpecIter ri( genreIter );
|
|
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
|
|
{
|
|
int nAdded = ri.GetAddedSet()->Size();
|
|
int nRemoved = ri.GetRemovedSet()->Size();
|
|
int nChanged = ri.GetNumChanged();
|
|
|
|
nAddedTotal += nAdded;
|
|
nRemovedTotal += nRemoved;
|
|
nChangedTotal += nChanged;
|
|
|
|
if( nAdded || nRemoved || nChanged )
|
|
{
|
|
// update the max severity
|
|
if( maxSeverityViolated < ri.GetAttr()->GetSeverity() )
|
|
maxSeverityViolated = ri.GetAttr()->GetSeverity();
|
|
|
|
nViolations += ( nAdded + nRemoved + nChanged );
|
|
}
|
|
}
|
|
}
|
|
|
|
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_VIOLATIONS_SHORT ) << _T(":") << nViolations;
|
|
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_MAX_SEV_SHORT ) << _T(":") << maxSeverityViolated;
|
|
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_ADDED_SHORT ) << _T(":") << nAddedTotal;
|
|
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_REMOVED_SHORT ) << _T(":") << nRemovedTotal;
|
|
sstrReport << _T(" ") << TSS_GetString( cTW, tw::STR_CHANGED_SHORT ) << _T(":") << nChangedTotal;
|
|
|
|
return sstrReport.str();
|
|
}
|
|
|
|
|
|
void cTextReportViewer::OutputParseableReport()
|
|
{
|
|
// iterate over all genres in a report
|
|
cFCOReportGenreIter genreIter( *mpReport );
|
|
for( genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next() )
|
|
{
|
|
SetUpForNewGenre( genreIter );
|
|
|
|
// iterate over all specs in a genre
|
|
cFCOReportSpecIter ri( genreIter );
|
|
for( ri.SeekBegin(); !ri.Done(); ri.Next() )
|
|
{
|
|
// iterate over all removed fcos
|
|
const cIterProxy<iFCOIter> pSetIterAdded = ri.GetAddedSet()->GetIter();
|
|
for( pSetIterAdded->SeekBegin(); !pSetIterAdded->Done(); pSetIterAdded->Next() )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_ADDED ) << _T(":\t");
|
|
(*mpOut) << mpCurNT->ToStringDisplay( pSetIterAdded->FCO()->GetName(), true ) << endl;
|
|
}
|
|
|
|
// iterate over all removed fcos
|
|
const cIterProxy<iFCOIter> pSetIterRemoved = ri.GetRemovedSet()->GetIter();
|
|
for( pSetIterRemoved->SeekBegin(); !pSetIterRemoved->Done(); pSetIterRemoved->Next() )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_REMOVED ) << _T(":\t");
|
|
(*mpOut) << mpCurNT->ToStringDisplay( pSetIterRemoved->FCO()->GetName(), true ) << endl;
|
|
}
|
|
|
|
// iterate over all changed fcos
|
|
cFCOReportChangeIter changedIter( ri );
|
|
for( changedIter.SeekBegin(); !changedIter.Done(); changedIter.Next() )
|
|
{
|
|
(*mpOut) << TSS_GetString( cTW, tw::STR_CHANGED ) << _T(":\t");
|
|
(*mpOut) << mpCurNT->ToStringDisplay( changedIter.GetNew()->GetName(), true ) << endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
char cTextReportViewer::PeekChar()
|
|
{
|
|
return char_traits<char>::
|
|
to_char_type(
|
|
mpIn->peek() );
|
|
}
|
|
|
|
|
|
// TODO:BAM -- man is this function sloppy!!!
|
|
// The only reason that this function would fail would be that the text report
|
|
// was corrupted, like um, binarily corrupted. (that is, anything a text editor
|
|
// would put in here would be readable)
|
|
void cTextReportViewer::GetChar()
|
|
{
|
|
cDebug d("cTextReportViewer::GetChar");
|
|
ASSERT( sizeof( mCurrentChar ) >= MB_CUR_MAX ); // mCurrentChar holds a mb character for current locale
|
|
|
|
// initialize mCurrentChar
|
|
mCurrentCharSize = 0;
|
|
for( uint32 i = 0; i < sizeof( mCurrentChar ); i++ )
|
|
mCurrentChar[i] = 0;
|
|
|
|
static const std::istream::char_type eof =
|
|
std::char_traits< char >::to_char_type(std::char_traits< char >::eof() );
|
|
|
|
std::streampos pos = mpIn->tellg();
|
|
|
|
for( size_t nch = 0; nch < (size_t)MB_CUR_MAX; nch++ )
|
|
{
|
|
if( mpIn->eof() || PeekIsEOF() )
|
|
{
|
|
// should be first byte we read
|
|
if( nch != 0 )
|
|
throw eTextReportViewerReportCorrupt("Expected EOF");
|
|
|
|
if( PeekIsEOF() )
|
|
{
|
|
// get the eof char so that mpIn->eof() will return true
|
|
mpIn->get();
|
|
}
|
|
|
|
d.TraceDebug( _T("Found EOF\n") );
|
|
|
|
mCurrentChar[0] = eof;
|
|
mCurrentCharSize = 1;
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if( ! mpIn->good() )
|
|
{
|
|
d.TraceDebug( _T("Input stream error.\n") );
|
|
throw eTextReportViewerReportCorrupt("Input stream error");
|
|
}
|
|
|
|
// get character from input stream
|
|
std::istream::char_type ch = std::char_traits<char>::to_char_type(mpIn->get());
|
|
|
|
// add character to mb buffer
|
|
mCurrentChar[nch] = ch;
|
|
mCurrentCharSize++;
|
|
|
|
// have we found a complete mb character yet?
|
|
if( 0 <= mbtowc( NULL, mCurrentChar, mCurrentCharSize ) )
|
|
{
|
|
// completed a valid mb charactter
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// incomplete mb char -- keep reading in bytes
|
|
}
|
|
}
|
|
}
|
|
|
|
mpIn->seekg( pos );
|
|
std::istream::char_type c = std::char_traits<char>::to_char_type( mpIn->get() );
|
|
if( (unsigned char)c > 0x7f )
|
|
{
|
|
mCurrentChar[0] = c;
|
|
mCurrentChar[1] = 0;
|
|
mCurrentCharSize = 1;
|
|
return;
|
|
}
|
|
|
|
// sequence was not a valid mb character
|
|
// (searched MB_CUR_MAX chars and didn't find a complete mb character)
|
|
d.TraceDebug( _T("Invalid mb char found!\n") );
|
|
#ifdef _DEBUG
|
|
for( int j = 0; j < MB_CUR_MAX; j++ )
|
|
d.TraceDebug( _T("%u\n"), (size_t)(unsigned char)mCurrentChar[j] );
|
|
#endif
|
|
ASSERT( false );
|
|
throw eTextReportViewerReportCorrupt("Invalid multibyte sequence");
|
|
}
|
|
|
|
void cTextReportViewer::AppendChar( std::string& str )
|
|
{
|
|
for( size_t s = 0; s < mCurrentCharSize; s++ )
|
|
str += mCurrentChar[s];
|
|
}
|
|
|