// // 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 //############################################################################# // 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 ); }