// // 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. // /////////////////////////////////////////////////////////////////////////////// // fcoreport.h /////////////////////////////////////////////////////////////////////////////// #include "stdtw.h" #include "fcoreport.h" #include "fcoreportutil.h" #include "fco/fcospeclist.h" #include "fco/fcosetimpl.h" #include "fco/fcosetws.h" #include "fco/fcospecutil.h" #include "fco/fcopropvector.h" #include "core/serializer.h" #include "core/debug.h" #include "core/errorbucketimpl.h" #include "fco/iterproxy.h" #include "core/hashtable.h" #include "core/fsservices.h" #include "fco/fcospecattr.h" #include "core/fileheader.h" #include "headerinfo.h" #include //============================================================================= // class cFCOReport_i //============================================================================= class cFCOReport_i { public: cFCOReport_i(); ~cFCOReport_i(); // note that all iFCOs and all iFCOSpecs in this structure are AddRef()ed when added // and will be Release()d when deleted. struct cChangeNode { const iFCO* mpOldFCO; const iFCO* mpNewFCO; cFCOPropVector mChangeVector; cChangeNode(); cChangeNode(const cChangeNode& rhs); ~cChangeNode(); void Clear(); }; struct cNode { iFCOSpec* mpSpec; // _must_ be valid (non-0) const cFCOSpecAttr* mpSpecAttr; // _must_ be valid (non-0) cFCOSetWS mAdded; // these two sets make sure their fcos are in mpSpec. cFCOSetWS mRemoved; std::list mChanged; cErrorQueue mErrorQueue; int32 mnObjectsScanned; cNode(); cNode(const cNode& rhs); ~cNode(); void Clear(); }; typedef std::list SpecList; struct cGenreNode { cGenreNode() {}; cGenreNode( const cGenreNode& rhs ); cFCOReportGenreHeader mGenreHeader; cGenre::Genre mGenre; SpecList mSpecList; }; typedef std::list GenreSpecList; // data members cErrorQueue mErrorQueue; // the general error queue GenreSpecList mGenreList; }; cFCOReport_i::cGenreNode::cGenreNode( const cFCOReport_i::cGenreNode& rhs ) : mGenreHeader( rhs.mGenreHeader ), mGenre( rhs.mGenre ), mSpecList( rhs.mSpecList ) { } cFCOReport_i::cFCOReport_i() { } cFCOReport_i::~cFCOReport_i() { } cFCOReport_i::cChangeNode::cChangeNode() : mpOldFCO(0), mpNewFCO(0) { } cFCOReport_i::cChangeNode::cChangeNode(const cFCOReport_i::cChangeNode& rhs) { mpOldFCO = rhs.mpOldFCO; if (mpOldFCO) mpOldFCO->AddRef(); mpNewFCO = rhs.mpNewFCO; if (mpNewFCO) mpNewFCO->AddRef(); mChangeVector = rhs.mChangeVector; } cFCOReport_i::cChangeNode::~cChangeNode() { Clear(); } void cFCOReport_i::cChangeNode::Clear() { if(mpOldFCO) mpOldFCO->Release(); if(mpNewFCO) mpNewFCO->Release(); mpOldFCO = mpNewFCO = 0; } cFCOReport_i::cNode::cNode() : mpSpec(0), mpSpecAttr(0), mAdded(0), mRemoved(0), mnObjectsScanned(0) { } cFCOReport_i::cNode::cNode(const cFCOReport_i::cNode& rhs) : mAdded(rhs.mAdded), mRemoved(rhs.mRemoved) { mpSpec = rhs.mpSpec; if (mpSpec) mpSpec->AddRef(); mpSpecAttr = rhs.mpSpecAttr; if (mpSpecAttr) mpSpecAttr->AddRef(); mChanged = rhs.mChanged; mErrorQueue = rhs.mErrorQueue; mnObjectsScanned = rhs.mnObjectsScanned; } cFCOReport_i::cNode::~cNode() { Clear(); } void cFCOReport_i::cNode::Clear() { mAdded.Clear(); mAdded.SetSpec(0); mRemoved.Clear(); mRemoved.SetSpec(0); mChanged.clear(); mErrorQueue.Clear(); if(mpSpec) mpSpec->Release(); if(mpSpecAttr) mpSpecAttr->Release(); mpSpec = 0; mpSpecAttr = 0; mnObjectsScanned = 0; } //============================================================================= // cFCOReportGenreIter //============================================================================= class cFCOReportGenreIter_i { public: cFCOReport_i::GenreSpecList* mpList; cFCOReport_i::GenreSpecList::iterator mIter; }; cFCOReportGenreIter::cFCOReportGenreIter(const cFCOReport& report) { mpData = new cFCOReportGenreIter_i; mpData->mpList = &report.mpData->mGenreList; mpData->mIter = mpData->mpList->begin(); } cFCOReportGenreIter::cFCOReportGenreIter(const cFCOReportGenreIter& rhs) { mpData = new cFCOReportGenreIter_i; *this = rhs; } cFCOReportGenreIter::~cFCOReportGenreIter() { delete mpData; } void cFCOReportGenreIter::operator=(const cFCOReportGenreIter& rhs) { mpData->mpList = rhs.mpData->mpList; mpData->mIter = rhs.mpData->mIter; } // iteration methods void cFCOReportGenreIter::SeekBegin() const { mpData->mIter = mpData->mpList->begin(); } void cFCOReportGenreIter::Next() const { ++mpData->mIter; } bool cFCOReportGenreIter::Done() const { return mpData->mIter == mpData->mpList->end(); } // seeks to specific genre. Returns false if genre does not exist in report bool cFCOReportGenreIter::SeekToGenre(cGenre::Genre genre) { for (mpData->mIter = mpData->mpList->begin(); ; ++mpData->mIter) { if (mpData->mIter == mpData->mpList->end()) return false; if (mpData->mIter->mGenre == genre) break; } return true; } // removes the entire genre from report void cFCOReportGenreIter::Remove() { ASSERT(!Done()); mpData->mIter = mpData->mpList->erase(mpData->mIter); } cFCOReportGenreHeader& cFCOReportGenreIter::GetGenreHeader() const { return mpData->mIter->mGenreHeader; } // Get current genre cGenre::Genre cFCOReportGenreIter::GetGenre() const { if (mpData->mIter == mpData->mpList->end()) { ASSERT(false); return cGenre::GENRE_INVALID; } return mpData->mIter->mGenre; } //============================================================================= // cFCOReportSpecIter //============================================================================= class cFCOReportSpecIter_i { public: cFCOReport_i::SpecList* mpList; cFCOReport_i::SpecList::iterator mIter; }; ///////////////////////// // ctor and dtor ///////////////////////// cFCOReportSpecIter::cFCOReportSpecIter(const cFCOReport& report, cGenre::Genre genre) { mpData = 0; cFCOReport_i::GenreSpecList::iterator genreIter; for (genreIter = report.mpData->mGenreList.begin(); ; ++genreIter) { if (genreIter == report.mpData->mGenreList.end()) { ASSERT(false); THROW_INTERNAL("fcoreport.cpp"); } if (genreIter->mGenre == genre) break; } mpData = new cFCOReportSpecIter_i(); mpData->mpList = &genreIter->mSpecList; mpData->mIter = mpData->mpList->begin(); } cFCOReportSpecIter::cFCOReportSpecIter(cFCOReport& report, cGenre::Genre genre) { mpData = 0; cFCOReport_i::GenreSpecList::iterator genreIter; for (genreIter = report.mpData->mGenreList.begin(); ; ++genreIter) { if (genreIter == report.mpData->mGenreList.end()) { // add an empty spec to the report report.AddSpec(genre, 0, 0, 0); genreIter = report.mpData->mGenreList.end(); --genreIter; ASSERT(genreIter->mGenre == genre); } if (genreIter->mGenre == genre) break; } mpData = new cFCOReportSpecIter_i(); mpData->mpList = &genreIter->mSpecList; mpData->mIter = mpData->mpList->begin(); } cFCOReportSpecIter::cFCOReportSpecIter(const cFCOReportGenreIter& genreIter) { mpData = new cFCOReportSpecIter_i(); mpData->mpList = &genreIter.mpData->mIter->mSpecList; mpData->mIter = mpData->mpList->begin(); } cFCOReportSpecIter::~cFCOReportSpecIter() { delete mpData; } cFCOReportSpecIter::cFCOReportSpecIter(const cFCOReportSpecIter& rhs) { mpData = new cFCOReportSpecIter_i(); *this = rhs; } void cFCOReportSpecIter::operator=(const cFCOReportSpecIter& rhs) { if (mpData == 0) mpData = new cFCOReportSpecIter_i(); mpData->mpList = rhs.mpData->mpList; mpData->mIter = rhs.mpData->mIter; } int cFCOReportSpecIter::GetNumChanged() const { ASSERT(! Done()); return mpData ? mpData->mIter->mChanged.size() : 0; } void cFCOReportSpecIter::SetObjectsScanned( int nObjectsScanned ) { ASSERT(mpData != 0); if (mpData == 0) THROW_INTERNAL("fcoreport.cpp"); mpData->mIter->mnObjectsScanned = nObjectsScanned; } int cFCOReportSpecIter::GetObjectsScanned() const { ASSERT(! Done()); return mpData ? mpData->mIter->mnObjectsScanned : 0; } void cFCOReportSpecIter::Remove() { ASSERT(! Done()); mpData->mIter->Clear(); mpData->mIter = mpData->mpList->erase(mpData->mIter); } ///////////////////////// // iteration methods ///////////////////////// void cFCOReportSpecIter::SeekBegin() const { if (mpData) mpData->mIter = mpData->mpList->begin(); } void cFCOReportSpecIter::Next() const { ASSERT(mpData != 0); mpData->mIter++; } bool cFCOReportSpecIter::Done() const { return mpData ? (mpData->mIter == mpData->mpList->end()) : true; } const iFCOSpec* cFCOReportSpecIter::GetSpec() const { ASSERT(! Done()); return mpData ? (mpData->mIter->mpSpec) : 0; } bool cFCOReportSpecIter::SeekToSpec(const iFCOSpec* pSpec) { if (mpData) for(mpData->mIter = mpData->mpList->begin(); mpData->mIter != mpData->mpList->end(); mpData->mIter++) { if(iFCOSpecUtil::FCOSpecEqual(*mpData->mIter->mpSpec, *pSpec)) return true; } return false; } const cFCOSpecAttr* cFCOReportSpecIter::GetAttr() const { ASSERT(! Done()); return mpData ? (mpData->mIter->mpSpecAttr) : 0; } const cErrorQueue* cFCOReportSpecIter::GetErrorQueue() const { ASSERT(! Done()); return mpData ? (&mpData->mIter->mErrorQueue) : 0; } cErrorQueue* cFCOReportSpecIter::GetErrorQueue() { ASSERT(! Done()); return mpData ? (&mpData->mIter->mErrorQueue) : 0; } const iFCOSet* cFCOReportSpecIter::GetAddedSet() const { ASSERT(! Done()); return mpData ? (&mpData->mIter->mAdded) : 0; } iFCOSet* cFCOReportSpecIter::GetAddedSet() { ASSERT(! Done()); return mpData ? (&mpData->mIter->mAdded) : 0; } const iFCOSet* cFCOReportSpecIter::GetRemovedSet() const { ASSERT(! Done()); return mpData ? (&mpData->mIter->mRemoved) : 0; } iFCOSet* cFCOReportSpecIter::GetRemovedSet() { ASSERT(! Done()); return mpData ? (&mpData->mIter->mRemoved) : 0; } //============================================================================= // class cFCOReportChangeIter //============================================================================= class cFCOReportChangeIter_i { public: std::list* mpList; std::list::iterator mIter; }; cFCOReportChangeIter::cFCOReportChangeIter(const cFCOReportSpecIter& specIter) { ASSERT(!specIter.Done()); mpData = new cFCOReportChangeIter_i; SetSpecIter(specIter); } cFCOReportChangeIter::cFCOReportChangeIter(const cFCOReportChangeIter& rhs) { mpData = new cFCOReportChangeIter_i; *this = rhs; } cFCOReportChangeIter::~cFCOReportChangeIter() { delete mpData; } void cFCOReportChangeIter::operator=(const cFCOReportChangeIter& rhs) { mpData->mpList = rhs.mpData->mpList; mpData->mIter = rhs.mpData->mIter; } void cFCOReportChangeIter::SetSpecIter(const cFCOReportSpecIter& specIter) { ASSERT(! specIter.Done()); mpData->mpList = &specIter.mpData->mIter->mChanged; mpData->mIter = mpData->mpList->begin(); } /////////////////////////////////////////////////////////////////////////////// // Remove /////////////////////////////////////////////////////////////////////////////// void cFCOReportChangeIter::Remove() { ASSERT(! Done()); mpData->mIter->Clear(); mpData->mIter = mpData->mpList->erase(mpData->mIter); } void cFCOReportChangeIter::SeekBegin() const { ASSERT(mpData->mpList != 0); mpData->mIter = mpData->mpList->begin(); } void cFCOReportChangeIter::Next() const { ASSERT(mpData->mpList != 0); mpData->mIter++; } bool cFCOReportChangeIter::Done() const { ASSERT(mpData->mpList != 0); if(! mpData->mpList) return true; return (mpData->mIter == mpData->mpList->end()); } const iFCO* cFCOReportChangeIter::GetOld() const { ASSERT(! Done()); return (mpData->mIter->mpOldFCO); } const iFCO* cFCOReportChangeIter::GetNew() const { ASSERT(! Done()); return (mpData->mIter->mpNewFCO); } const cFCOPropVector& cFCOReportChangeIter::GetChangeVector() const { ASSERT(! Done()); return (mpData->mIter->mChangeVector); } //============================================================================= // class cFCOReport //============================================================================= IMPLEMENT_TYPEDSERIALIZABLE(cFCOReport, _T("cFCOReport"), 0, 1); cFCOReport::cFCOReport() { mpData = new cFCOReport_i; } cFCOReport::~cFCOReport() { ClearReport(); delete mpData; } //////////////////////////////////////////////////////////// cErrorQueue* cFCOReport::GetErrorQueue() { return &mpData->mErrorQueue; } const cErrorQueue* cFCOReport::GetErrorQueue() const { return &mpData->mErrorQueue; } /////////////////////////////////////////////////////////////////////////////// // GetNumSpecs /////////////////////////////////////////////////////////////////////////////// int cFCOReport::GetNumSpecs(cGenre::Genre genre) const { cFCOReport_i::GenreSpecList::iterator genreIter; for (genreIter = mpData->mGenreList.begin(); ; ++genreIter) { if (genreIter == mpData->mGenreList.end()) { return 0; } if (genreIter->mGenre == genre) break; } return genreIter->mSpecList.size(); } /////////////////////////////////////////////////////////////////////////////// // ClearReport /////////////////////////////////////////////////////////////////////////////// void cFCOReport::ClearReport() { mpData->mGenreList.clear(); } /////////////////////////////////////////////////////////////////////////////// // AddSpec /////////////////////////////////////////////////////////////////////////////// void cFCOReport::AddSpec(cGenre::Genre genre, const iFCOSpec* pSpec, const cFCOSpecAttr* pAttr, cFCOReportSpecIter* pIter) { // look up the genre cFCOReport_i::GenreSpecList::iterator genreIter; for (genreIter = mpData->mGenreList.begin(); ; ++genreIter) { if (genreIter == mpData->mGenreList.end()) { cFCOReport_i::cGenreNode newGenre; newGenre.mGenre = genre; mpData->mGenreList.push_back(newGenre); genreIter = mpData->mGenreList.end(); --genreIter; break; } if (genreIter->mGenre == genre) break; } if (pSpec == 0) { ASSERT(pAttr == 0); ASSERT(pIter == 0); return; } // make sure this spec doesn't overlap any others cFCOReport_i::SpecList::iterator specIter; for(specIter = genreIter->mSpecList.begin(); specIter != genreIter->mSpecList.end(); ++specIter) { // TODO -- what is the right action to take? if(iFCOSpecUtil::SpecsOverlap(pSpec, specIter->mpSpec)) { ASSERT(false); THROW_INTERNAL("fcoreport.cpp"); } } genreIter->mSpecList.push_back(cFCOReport_i::cNode()); cFCOReport_i::cNode& node = genreIter->mSpecList.back(); node.mpSpec = pSpec->Clone(); node.mpSpecAttr = pAttr; pAttr->AddRef(); node.mAdded.SetSpec(node.mpSpec); node.mRemoved.SetSpec(node.mpSpec); // fill out the iterator if (pIter && pIter->mpData && pIter->mpData->mpList == &genreIter->mSpecList) { pIter->mpData->mIter = genreIter->mSpecList.end(); pIter->mpData->mIter--; ASSERT(pIter->GetSpec() == node.mpSpec); } } /////////////////////////////////////////////////////////////////////////////// // AddChangedFCO /////////////////////////////////////////////////////////////////////////////// void cFCOReport::AddChangedFCO(const cFCOReportSpecIter& iter, const iFCO* pOldFCO, const iFCO* pNewFCO, const cFCOPropVector& changedProps) { cDebug d("cFCOReport::AddChangedFCO"); // make some assertions about the iterator #ifdef _DEBUG // make sure iter points to one of our spec lists cFCOReport_i::GenreSpecList::iterator genreIter; for (genreIter = mpData->mGenreList.begin(); ; ++genreIter) { if (genreIter == mpData->mGenreList.end()) { ASSERT(false); THROW_INTERNAL("fcoreport.cpp"); } if (&genreIter->mSpecList == iter.mpData->mpList) break; } #endif ASSERT(! iter.Done()); // make sure it points to something valid // some sanity checking for the fco names... ASSERT(pOldFCO->GetName().IsEqual(pNewFCO->GetName())); ASSERT((iter.GetSpec()->SpecContainsFCO(pOldFCO->GetName()))); iter.mpData->mIter->mChanged.push_back(cFCOReport_i::cChangeNode()); cFCOReport_i::cChangeNode& changeNode = iter.mpData->mIter->mChanged.back(); changeNode.mpOldFCO = pOldFCO; changeNode.mpNewFCO = pNewFCO; changeNode.mChangeVector = changedProps; pOldFCO->AddRef(); pNewFCO->AddRef(); } /////////////////////////////////////////////////////////////////////////////// // iSerializable interface /////////////////////////////////////////////////////////////////////////////// void cFCOReport::Read(iSerializer* pSerializer, int32 version) { if (version > Version()) ThrowAndAssert(eSerializerVersionMismatch(_T("Report Read"))); ClearReport(); // read in main error queue pSerializer->ReadObject(&mpData->mErrorQueue); // read in the genres int32 genreIter, genreCount; int32 specIter, specCount; pSerializer->ReadInt32(genreCount); for (genreIter = 0; genreIter < genreCount; genreIter++) { cFCOReport_i::cGenreNode newGenre; int32 genre; // TODO:BAM -- this used to be int16, so take care of backwards compatability pSerializer->ReadInt32(genre); newGenre.mGenre = (cGenre::Genre)genre; pSerializer->ReadObject( &newGenre.mGenreHeader ); pSerializer->ReadInt32(specCount); for (specIter = 0; specIter < specCount; specIter++) { newGenre.mSpecList.push_back(cFCOReport_i::cNode()); cFCOReport_i::cNode& node = newGenre.mSpecList.back(); node.mpSpec = static_cast (pSerializer->ReadObjectDynCreate()); node.mpSpecAttr = static_cast(pSerializer->ReadObjectDynCreate()); pSerializer->ReadObject(&node.mErrorQueue); pSerializer->ReadObject(&node.mAdded); pSerializer->ReadObject(&node.mRemoved); pSerializer->ReadInt32( node.mnObjectsScanned ); node.mAdded.SetSpec(node.mpSpec); node.mRemoved.SetSpec(node.mpSpec); int32 changeSize; pSerializer->ReadInt32(changeSize); for(int j=0; j(pSerializer->ReadObjectDynCreate()); cnode.mpNewFCO = static_cast(pSerializer->ReadObjectDynCreate()); cnode.mChangeVector.Read(pSerializer); } } mpData->mGenreList.push_back(newGenre); } // TODO -- we should do some kind of test here to ensure that all the data we read // is consistant (ie -- no overlapping specs) } void cFCOReport::Write(iSerializer* pSerializer) const { cFCOReport_i::GenreSpecList::iterator genreIter; cFCOReport_i::SpecList::iterator specIter; // write out the main error queue pSerializer->WriteObject(&mpData->mErrorQueue); // iteratate over genres pSerializer->WriteInt32(mpData->mGenreList.size()); for (genreIter = mpData->mGenreList.begin(); genreIter != mpData->mGenreList.end(); ++genreIter) { // TODO:BAM -- this used to be int16, so take care of backwards compatability pSerializer->WriteInt32(genreIter->mGenre); pSerializer->WriteObject( &genreIter->mGenreHeader ); // write each node out... pSerializer->WriteInt32(genreIter->mSpecList.size()); for (specIter = genreIter->mSpecList.begin(); specIter != genreIter->mSpecList.end(); ++specIter) { pSerializer->WriteObjectDynCreate(specIter->mpSpec); pSerializer->WriteObjectDynCreate(specIter->mpSpecAttr); pSerializer->WriteObject(&specIter->mErrorQueue); pSerializer->WriteObject(&specIter->mAdded); pSerializer->WriteObject(&specIter->mRemoved); pSerializer->WriteInt32( specIter->mnObjectsScanned ); std::list::iterator changedIter; pSerializer->WriteInt32(specIter->mChanged.size()); for(changedIter = specIter->mChanged.begin(); changedIter != specIter->mChanged.end(); ++changedIter) { pSerializer->WriteObjectDynCreate(changedIter->mpOldFCO); pSerializer->WriteObjectDynCreate(changedIter->mpNewFCO); changedIter->mChangeVector.Write(pSerializer); } } } } /////////////////////////////////////////////////////////////////////////////// // GetFileHeaderID() /////////////////////////////////////////////////////////////////////////////// struct cFCOReportFHID { cFileHeaderID* ReportID; cFCOReportFHID() { ReportID = 0; } ~cFCOReportFHID() { delete ReportID; } } gFCOReportFHID; const cFileHeaderID& cFCOReport::GetFileHeaderID() { if (gFCOReportFHID.ReportID == 0) gFCOReportFHID.ReportID = new cFileHeaderID(CLASS_TYPE(cFCOReport).AsString()); // sanity check ASSERT(*gFCOReportFHID.ReportID == cFileHeaderID(CLASS_TYPE(cFCOReport).AsString())); return *gFCOReportFHID.ReportID; } void cFCOReport::TraceContents(int dl) const { if(dl < 0) dl = cDebug::D_DEBUG; cDebug d("cFCOReport:"); d.TraceDebug("Global Error Queue:\n"); mpData->mErrorQueue.TraceContents(dl); cFCOReport_i::GenreSpecList::iterator genreIter; cFCOReport_i::SpecList::iterator specIter; for (genreIter = mpData->mGenreList.begin(); genreIter != mpData->mGenreList.end(); ++genreIter) { d.Trace(dl, "> Genre [%d]:\n", (int)genreIter->mGenre); int specCount = 0; for (specIter = genreIter->mSpecList.begin(); specIter != genreIter->mSpecList.end(); ++specIter, ++specCount) { d.Trace(dl, ">>> Spec [%d]:\n", specCount); ASSERT(specIter->mpSpec != 0); specIter->mpSpec->TraceContents(dl); specIter->mpSpecAttr->TraceContents(dl); specIter->mErrorQueue.TraceContents(dl); // trace out added & removed files d.TraceDebug(">>> Added Files:\n"); specIter->mAdded.TraceContents(dl); d.TraceDebug(">>> Removed Files:\n"); specIter->mRemoved.TraceContents(dl); // trace out changed files d.TraceDebug(">>> Changed Files:\n"); std::list::iterator changedIter; int changeCounter = 0; for (changedIter = specIter->mChanged.begin(); changedIter != specIter->mChanged.end(); ++changedIter, ++changeCounter) { d.Trace(dl, ">>>>> Changed fco [%d]\n", changeCounter); d.Trace(dl, ">>>>> Old FCO:\n"); (*changedIter).mpOldFCO->TraceContents(dl); d.Trace(dl, ">>>>> New FCO:\n"); (*changedIter).mpNewFCO->TraceContents(dl); (*changedIter).mChangeVector.TraceContents(dl); } } } }