tripwire-open-source/src/fco/fconame.cpp

636 lines
20 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.
//
///////////////////////////////////////////////////////////////////////////////
// fconame.cpp
///////////////////////////////////////////////////////////////////////////////
#include "stdfco.h"
#include "fconame.h"
#include "core/serializer.h"
#include "fconametbl.h"
#include "core/refcountobj.h"
#include "twfactory.h"
#include "core/errorutil.h"
#include "core/ntmbs.h"
#include <vector>
//#############################################################################
// cFCOName_i -- an implementation of a cFCOName -- this object is refrence
// counted (so copies are cheap) and contains a linked list of cFCOTableNodes
// as well as the static instance of the cFCONameTbl.
//#############################################################################
class cFCOName_i : public cRefCountObj
{
public:
cFCOName::ListType mNames;
~cFCOName_i() { ClearList(); }
void ClearList();
// releases all the names in mNames and clears the list
// the single name table
static cFCONameTbl msNameTbl;
};
///////////////////////////////////////////////////
// ClearList()
///////////////////////////////////////////////////
inline void cFCOName_i::ClearList()
{
cFCOName::ListType::iterator i;
for(i = mNames.begin(); i != mNames.end(); ++i)
(*i)->Release();
mNames.clear();
}
cFCONameTbl cFCOName_i::msNameTbl;
//#############################################################################
// cFCOName
//#############################################################################
IMPLEMENT_TYPEDSERIALIZABLE(cFCOName, _T("cFCOName"), 0, 1)
///////////////////////////////////////////////////////////////////////////////
// ClearNameTable -- IMPORTANT!!! This should only be called when you are 100%
// positive no more operations involving fco names will occur.
///////////////////////////////////////////////////////////////////////////////
void cFCOName::ClearNameTable()
{
cFCOName_i::msNameTbl.Clear();
}
///////////////////////////////////////////////////////////////////////////////
// ctor, dtor
///////////////////////////////////////////////////////////////////////////////
cFCOName::cFCOName(iFCONameInfo* pNI) :
iTypedSerializable(), mpPathName(0), mDelimiter('/')
{
SetNameInfo(pNI);
mpPathName = new cFCOName_i;
#ifdef _DEBUG
mDebugStrName = AsString();
cDebug d("cFCOName::cFCOName(iFCONameInfo*)");
d.TraceNever(_T("constructing %X:%X %s (refcount=%d)\n"), this, mpPathName, mDebugStrName.c_str(), mpPathName->GetRefCount());
#endif
}
cFCOName::cFCOName(const cFCOName& rhs) :
iTypedSerializable(),
mpPathName(rhs.mpPathName),
mDelimiter(rhs.mDelimiter),
mbCaseSensitive(rhs.mbCaseSensitive)
{
mpPathName->AddRef();
#ifdef _DEBUG
mDebugStrName = AsString();
cDebug d("cFCOName::cFCOName(cFCOName&)");
d.TraceNever(_T("constructing %X:%X %s (refcount=%d)\n"), this, mpPathName, mDebugStrName.c_str(), mpPathName->GetRefCount());
#endif
}
cFCOName::cFCOName(const TSTRING& rhs, iFCONameInfo* pNI) :
iTypedSerializable(), mpPathName(0), mDelimiter('/')
{
SetNameInfo(pNI);
mpPathName = new cFCOName_i;
ParseString(rhs.c_str());
#ifdef _DEBUG
mDebugStrName = AsString();
cDebug d("cFCOName::cFCOName(cFCOName&,iFCONameInfo*)");
d.TraceNever(_T("constructing %X:%X %s (refcount=%d)\n"), this, mpPathName, mDebugStrName.c_str(), mpPathName->GetRefCount());
#endif
}
cFCOName::cFCOName(const TCHAR* rhs, iFCONameInfo* pNI) :
iTypedSerializable(), mpPathName(0), mDelimiter('/')
{
SetNameInfo(pNI);
mpPathName = new cFCOName_i;
ParseString(rhs);
#ifdef _DEBUG
mDebugStrName = AsString();
cDebug d("cFCOName::cFCOName(cFCOName&,iFCONameInfo*)");
d.TraceNever(_T("constructing %X:%X %s (refcount=%d)\n"), this, mpPathName, mDebugStrName.c_str(), mpPathName->GetRefCount());
#endif
}
cFCOName::~cFCOName()
{
#ifdef _DEBUG
cDebug d("cFCOName::~cFCOName()");
d.TraceNever(_T("destructing %X:%X %s (refcount=%d)\n"), this, mpPathName, mDebugStrName.c_str(), mpPathName->GetRefCount());
#endif
mpPathName->Release();
}
void cFCOName::SetNameInfo(iFCONameInfo* pNI)
{
if( pNI )
{
mbCaseSensitive = pNI->IsCaseSensitive();
mDelimiter = pNI->GetDelimitingChar();
}
else
{
mbCaseSensitive = iTWFactory::GetInstance()->GetNameInfo()->IsCaseSensitive();
mDelimiter = iTWFactory::GetInstance()->GetNameInfo()->GetDelimitingChar();
}
#ifdef _DEBUG
if( mpPathName != NULL ) // this could be called from the constructor before this is initialized.
mDebugStrName = AsString();
#endif
}
///////////////////////////////////////////////////////////////////////////////
// operator=
///////////////////////////////////////////////////////////////////////////////
void cFCOName::operator = (const cFCOName& rhs)
{
mpPathName->Release();
// TODO -- I am sure this won't work (const-ness)
mpPathName = rhs.mpPathName;
mpPathName->AddRef();
mDelimiter = rhs.mDelimiter;
mbCaseSensitive = rhs.mbCaseSensitive;
#ifdef _DEBUG
mDebugStrName = AsString();
#endif
}
void cFCOName::operator = (const TSTRING& rhs)
{
*this = rhs.c_str();
#ifdef _DEBUG
mDebugStrName = AsString();
#endif
}
void cFCOName::operator = (const TCHAR* rhs)
{
// if I have the only handle on this vector, I can reuse it
// otherwise, I have to release it.
if(mpPathName->GetRefCount() != 1)
{
mpPathName->Release();
mpPathName = new cFCOName_i;
}
ParseString(rhs);
#ifdef _DEBUG
mDebugStrName = AsString();
#endif
}
void cFCOName::ParseString( const TCHAR* pszin )
{
ASSERT(mpPathName != 0);
ASSERT(pszin != 0);
mpPathName->ClearList();
const TCHAR* at = (pszin + 0);
const TCHAR* begin = at;
const TCHAR* end = at;
int components = 0;
while (*end)
++end;
while (at < end)
{
while (*at && !(*at == mDelimiter) && (at < end))
at++;
TSTRING name(begin, at);
if (name.length() > 0 || components == 0)
{
cFCONameTblNode* pNode =
cFCOName_i::msNameTbl.CreateNode(name);
mpPathName->mNames.push_back(pNode);
}
components++;
at++;
begin=at;
//begin = (at = tss::strinc(at));
}
}
///////////////////////////////////////////////////////////////////////////////
// AsString
///////////////////////////////////////////////////////////////////////////////
TSTRING cFCOName::AsString() const
{
TSTRING str;
str = _T("");
ASSERT(mpPathName != 0);
// this kind of stinks, but I have to special case the root dir ("/")
// if I don't, it appears as an empty string
// 15 Oct -- I also had to add a yucky hack for c:\ in windows
//
// 13 Jan 99 mdb -- I have decided that all fconames that are one item long should be
// considered "root items" and should thus be displayed with a trailing delimiting character.
//
if(mpPathName->mNames.size() == 1)
{
str = (*mpPathName->mNames.begin())->GetString();
str += mDelimiter;
return str;
}
// end ugly root dir hacks ...
ListType::iterator i = mpPathName->mNames.begin();
while(i != mpPathName->mNames.end())
{
TSTRING current = (*i)->GetString();
// the loop is constructed in this odd fashion because I don't want a trailing mDelimiter
str += current;
i++;
if(i != mpPathName->mNames.end() && current != "/")
str += mDelimiter;
}
return str;
}
///////////////////////////////////////////////////////////////////////////////
// GetShortName
///////////////////////////////////////////////////////////////////////////////
const TCHAR* cFCOName::GetShortName() const
{
ASSERT( ! mpPathName->mNames.empty() );
if( mpPathName->mNames.empty() )
return 0;
return ( mpPathName->mNames.back()->GetString() );
}
///////////////////////////////////////////////////////////////////////////////
// Clear
///////////////////////////////////////////////////////////////////////////////
void cFCOName::Clear()
{
//TODO -- I could probably implement this a little cleaner...
//
while( ! mpPathName->mNames.empty() )
{
Pop();
}
#ifdef _DEBUG
mDebugStrName = AsString();
#endif
}
///////////////////////////////////////////////////////////////////////////////
// GetRelationship
///////////////////////////////////////////////////////////////////////////////
cFCOName::Relationship cFCOName::GetRelationship(const cFCOName& rhs) const
{
ListType::iterator myIter, rhsIter;
// get the easy equality out of the case first...
if(mpPathName == rhs.mpPathName)
return REL_EQUAL;
// if either name is case sensitive, we will do a case sensitive compare
bool bCaseSensitive = (IsCaseSensitive() || rhs.IsCaseSensitive());
bool bEqual;
for(myIter = mpPathName->mNames.begin(), rhsIter = rhs.mpPathName->mNames.begin();
(myIter != mpPathName->mNames.end() && rhsIter != rhs.mpPathName->mNames.end());
myIter++, rhsIter++)
{
if(bCaseSensitive)
bEqual = (*myIter == *rhsIter);
else
bEqual = ((*myIter)->GetLowercaseNode() == (*rhsIter)->GetLowercaseNode());
if(! bEqual)
return REL_UNRELATED;
}
// if we got this far, one is above another, or they are equal...
if((myIter == mpPathName->mNames.end()) && (rhsIter == rhs.mpPathName->mNames.end()))
return REL_EQUAL;
else if(myIter == mpPathName->mNames.end())
// I am shorter; I am above rhs
return REL_ABOVE;
else
return REL_BELOW;
}
///////////////////////////////////////////////////////////////////////////////
// Read
// TODO -- serialize the hash table and nodes instead of reading and writing
// as a string
///////////////////////////////////////////////////////////////////////////////
void cFCOName::Read(iSerializer* pSerializer, int32 version)
{
if (version > Version())
ThrowAndAssert(eSerializerVersionMismatch(_T("FCO Name Read")));
TSTRING str;
pSerializer->ReadString(str);
int16 dummy = 0;
// serialize the delimiter
pSerializer->ReadInt16( dummy ); // delimiter, but it's always '/' anyway in OST.
mDelimiter = '/';
// read the case-sensitiveness
pSerializer->ReadInt16(dummy);
if(dummy == 0)
mbCaseSensitive = false;
else
mbCaseSensitive = true;
ParseString(str.c_str());
#ifdef _DEBUG
mDebugStrName = AsString();
#endif
}
///////////////////////////////////////////////////////////////////////////////
// Write
// TODO -- serialize the hash table and nodes instead of reading and writing
// as a string
///////////////////////////////////////////////////////////////////////////////
void cFCOName::Write(iSerializer* pSerializer) const
{
pSerializer->WriteString(AsString());
// serialize the delimiter
unsigned short wc = (unsigned short)'/';
pSerializer->WriteInt16(wc);
pSerializer->WriteInt16( mbCaseSensitive ? (int16)1 : (int16)0);
}
///////////////////////////////////////////////////////////////////////////////
// CopyOnModify -- if the refrence count on mpPathName is > 1, release it and
// allocate our own copy of it
///////////////////////////////////////////////////////////////////////////////
void cFCOName::CopyOnModify()
{
if(mpPathName->GetRefCount() > 1)
{
cFCOName_i* pOld= mpPathName;
mpPathName = new cFCOName_i;
ListType::iterator i;
for(i = pOld->mNames.begin(); i != pOld->mNames.end(); ++i)
{
(*i)->AddRef();
mpPathName->mNames.push_back(*i);
}
pOld->Release();
}
}
///////////////////////////////////////////////////////////////////////////////
// Push
///////////////////////////////////////////////////////////////////////////////
void cFCOName::Push(const TSTRING& str)
{
// we must copy the fconame if there is more than one refrence to it...
CopyOnModify();
cFCONameTblNode* pNode = cFCOName_i::msNameTbl.CreateNode(str);
mpPathName->mNames.push_back(pNode);
#ifdef _DEBUG
mDebugStrName = AsString();
#endif
}
///////////////////////////////////////////////////////////////////////////////
// Pop
///////////////////////////////////////////////////////////////////////////////
const TCHAR* cFCOName::Pop()
{
// we must copy the fconame if there is more than one refrence to it...
CopyOnModify();
ASSERT(GetSize() > 0);
cFCONameTblNode* pNode = mpPathName->mNames.back();
mpPathName->mNames.pop_back();
// I do this assertion because it should also be in the hash table
ASSERT(pNode->GetRefCount() > 1);
const TCHAR* ret = pNode->GetString();
pNode->Release();
#ifdef _DEBUG
mDebugStrName = AsString();
#endif
return ret;
}
///////////////////////////////////////////////////////////////////////////////
// PopFront
///////////////////////////////////////////////////////////////////////////////
const TCHAR* cFCOName::PopFront()
{
// we must copy the fconame if there is more than one reference to it...
CopyOnModify();
ASSERT(GetSize() > 0);
cFCONameTblNode* pNode = mpPathName->mNames.front();
cFCOName::ListType::iterator i = mpPathName->mNames.begin();
mpPathName->mNames.erase( i );
// I do this assertion because it should also be in the hash table
ASSERT(pNode->GetRefCount() > 1);
const TCHAR* ret = pNode->GetString();
pNode->Release();
#ifdef _DEBUG
mDebugStrName = AsString();
#endif
return ret;
}
///////////////////////////////////////////////////////////////////////////////
// GetSize
///////////////////////////////////////////////////////////////////////////////
int cFCOName::GetSize() const
{
return mpPathName->mNames.size();
}
///////////////////////////////////////////////////////////////////////////////
// operator< -- provides an arbitrary ordering to cFCONames. The algorithm I chose
// is like strcmp, except instead of comparing characters, I compare
// cFCONameTblNode* addresses
///////////////////////////////////////////////////////////////////////////////
bool cFCOName::operator<(const cFCOName& rhs) const
{
ListType::iterator myIter, rhsIter;
// if either name is case sensitive, we will do a case sensitive compare
bool bCaseSensitive = (IsCaseSensitive() || rhs.IsCaseSensitive());
// get the easy equality out of the case first...
if(mpPathName == rhs.mpPathName)
return false;
for(myIter = mpPathName->mNames.begin(), rhsIter = rhs.mpPathName->mNames.begin();
(myIter != mpPathName->mNames.end() && rhsIter != rhs.mpPathName->mNames.end());
myIter++, rhsIter++)
{
if(bCaseSensitive)
{
if (*myIter > *rhsIter)
return false;
else if (*myIter < *rhsIter)
return true;
}
else
{
// not case sensitive
if ((*myIter)->GetLowercaseNode() > (*rhsIter)->GetLowercaseNode())
return false;
else if ((*myIter)->GetLowercaseNode() < (*rhsIter)->GetLowercaseNode())
return true;
}
// if they are equal, keep going
}
// if we got this far, one is above another, or they are equal...
if(rhsIter == rhs.mpPathName->mNames.end())
// either I am longer of we are equal; so return false
return false;
return true;
}
//-----------------------------------------------------------------------------
// cFCONameIter
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// cFCONameIter
///////////////////////////////////////////////////////////////////////////////
cFCONameIter::cFCONameIter(const cFCOName& name)
: mName(name)
{
SeekBegin();
}
///////////////////////////////////////////////////////////////////////////////
// ~cFCONameIter
///////////////////////////////////////////////////////////////////////////////
cFCONameIter::~cFCONameIter()
{
}
///////////////////////////////////////////////////////////////////////////////
// GetSize
///////////////////////////////////////////////////////////////////////////////
int cFCONameIter::GetSize() const
{
return mName.mpPathName->mNames.size();
}
///////////////////////////////////////////////////////////////////////////////
// SeekBegin
///////////////////////////////////////////////////////////////////////////////
void cFCONameIter::SeekBegin()
{
mIter = mName.mpPathName->mNames.begin();
}
///////////////////////////////////////////////////////////////////////////////
// Next
///////////////////////////////////////////////////////////////////////////////
void cFCONameIter::Next()
{
mIter++;
}
///////////////////////////////////////////////////////////////////////////////
// Done
///////////////////////////////////////////////////////////////////////////////
bool cFCONameIter::Done() const
{
return ( mIter == mName.mpPathName->mNames.end() );
}
///////////////////////////////////////////////////////////////////////////////
// GetName
///////////////////////////////////////////////////////////////////////////////
const TCHAR* cFCONameIter::GetName() const
{
ASSERT( ! Done() );
return (*mIter)->GetString();
}
///////////////////////////////////////////////////////////////////////////////
// Prev
///////////////////////////////////////////////////////////////////////////////
void cFCONameIter::Prev()
{
mIter--;
}
///////////////////////////////////////////////////////////////////////////////
// Index
///////////////////////////////////////////////////////////////////////////////
int cFCONameIter::Index() const
{
ASSERT( ! Done() );
return ( mIter - mName.mpPathName->mNames.begin() );
}
///////////////////////////////////////////////////////////////////////////////
// SeekTo
///////////////////////////////////////////////////////////////////////////////
void cFCONameIter::SeekTo( int index )
{
ASSERT( (index >= 0) && (index < mName.GetSize()) );
mIter = ( mName.mpPathName->mNames.begin() + index );
}