//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 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.
//
///////////////////////////////////////////////////////////////////////////////
//dbdebug.cpp
//

#include "stdtw.h"

#include "dbdebug.h"
#include "db/hierdatabase.h"
#include "db/hierdbnode.h"
#include "db/blockrecordarray.h"
#include "db/blockfile.h"
#include "dbdatasource.h"
#include "fcodatabasefile.h"
#include "db/blockrecordfile.h"
#include "fco/twfactory.h"
#include "fco/fconametranslator.h"

#include "core/error.h"
#include "core/errorbucketimpl.h"
#include "core/debug.h"
#include "fco/genreswitcher.h"

#include "fco/fcopropdisplayer.h"
#include "fco/fcopropvector.h"
#include "fco/fcopropset.h"
#include "fco/fcoprop.h"

#include <utility>
#include <map>
#include <list>
#include <vector>
#include "core/tchar.h"
#include "core/types.h"

//////////////////////////////////////////////////////////////////////////////////////////////////
// UTILITY METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////
//
//static void util_PrintFCO(const iFCO* pFCO, iFCOPropDisplayer * pPropDisplayer);
static void util_InitBlockArray(cBlockRecordArray& block);
//static void util_MapHierRoot( std::map< std::pair< int, int>, int > dbMap );

//////////////////////////////////////////////////////////////////////////////////////////////////
// Insulated implementation:
//////////////////////////////////////////////////////////////////////////////////////////////////
struct cDbDebug_i
{
    cDbDebug_i(){};
    ~cDbDebug_i(){};

    typedef std::map<std::pair<int, int>, int> hierDbMap;
    // This is the data structure that we will use to match the information stored in a quantum database
    // with its hierarchical representation!  For now, it will hold the <int, int> tuples representing
    // valid offsets in each block (referenced by a block number).  The bool value will be set to false
    // on the first run, and then flipped to true for each entry that is found while traversing the
    // hierarchical database.
    typedef std::list<std::pair<int, int> > AddressList;

    hierDbMap mDbMap; // The map itself.
    AddressList
                mlMissingFromBlockFile; // A list for keeping track of nodes found in the hierarchy, but not the blockfile
    AddressList mlMissingFromHierarchy; // The opposite case.
};

cDbDebug::cDbDebug()
{
    mpData = new cDbDebug_i();
}

cDbDebug::~cDbDebug()
{
    delete mpData;
}


///////////////////////////////////////////////////////////////////////////////
// util_InitBlockArray
///////////////////////////////////////////////////////////////////////////////
static void util_InitBlockArray(cBlockRecordArray& block)
{
    if (!block.Initialized())
    {
        block.InitForExistingBlock();
    }
}

///////////////////////////////////////////////////////////////////////////////
// util_MapHierRoot : Map the Root and RootArray in the HierDatabase:
///////////////////////////////////////////////////////////////////////////////
static void util_MapHierRoot(std::map<std::pair<int, int>, int>* dbMap)
{
    cDbDebug_i::hierDbMap::iterator i = dbMap->find(std::pair<int, int>(0, 0));
    //dbMap->insert( cDbDebug_i::hierDbMap::value_type( std::pair< int, int > ( 0, 0 ), 1 ) );
    //
    // This insert statement should work to change the value, but it doesn't ????
    (*i).second = 1;

    i           = dbMap->find(std::pair<int, int>(0, 1));
    (*i).second = 1;

    i           = dbMap->find(std::pair<int, int>(0, 2));
    (*i).second = 1;
}

///////////////////////////////////////////////////////////////////////////////
// DisplayDbMap -- Displays the entire map.
///////////////////////////////////////////////////////////////////////////////
void cDbDebug::DisplayDbMap()
{
    int loop_count = 0;

    for (cDbDebug_i::hierDbMap::iterator i = cDbDebug::mpData->mDbMap.begin(); i != cDbDebug::mpData->mDbMap.end();
         ++i, ++loop_count)
    {
        TCOUT.width(6);
        TCOUT << (*i).first.first << _T(",") << (*i).first.second << _T(" ");
        TCOUT << _T("(") << (*i).second << _T(")");
        if ((loop_count % 5) == 0)
            TCOUT << std::endl;
    }
    TCOUT << std::endl;
}

/////////////////////////////////////////////////////////////////////////////////
// GetHierDbMap() -- Returns a pointer to the map
/////////////////////////////////////////////////////////////////////////////////
cDbDebug::hierDbMap* cDbDebug::GetHierDbMap(void)
{
    return &(mpData->mDbMap);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// Execute :
//      Drives the DebugDb mode of tripwire, which will only be used for internal use.
//////////////////////////////////////////////////////////////////////////////////////////////////////
void cDbDebug::Execute(cFCODatabaseFileIter& dbIter, const TSTRING& dbFile)
{

    cDebug   d("cDebugDb::Execute");
    cDbDebug DbDebug;
    //
    //A derived class with extra methods for obtaining blockfile information.
    cDebugHierDb* db;
    //
    // Cast the database object that we have inherited from the "common" command line
    // information to a cDebugHierDb.  This will expose all the information that we
    // need to debug the database.
    //
    db = static_cast<cDebugHierDb*>(&(dbIter.GetDb()));

    try
    {
        cDebugHierDbIter pIter(db);

        //
        //First, map the state of the underlying quantyum database.  Store this info. in
        //the hierDbMap.
        //
        DbDebug.MapQuantumDatabase(*db);

        //DbDebug.DisplayDbMap(); //TODO : get rid of these display calls.

        //
        // Uncomment ManipDb() to allow manipulation of the database
        // This may be handy if one does not want to do a full update...
        //DbDebug.ManipDb( dbIter );

        // Next, traverse the hierarchical overlay and "tag" all entries in the map that
        // we see:
        ASSERT(pIter.AtRoot());
        //
        // check to make sure we are at the root of the hierarchy, if so, map the root
        // before calling the recursive traversal function.
        //
        util_MapHierRoot(DbDebug.GetHierDbMap());

        DbDebug.TraverseHierarchy(pIter /*, db*/);

        //DbDebug.DisplayDbMap(); //TODO: get rid of these display calls.
        //
        // Finally, iterate over the map and see if there is any data that the hierarchy
        // does not account for:
        //
        DbDebug.CongruencyTest();
        //
        // Output the results
        //
        DbDebug.OutputResults();

    } //try

    catch (eError& e)
    {
        cErrorReporter::PrintErrorMsg(e);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////
// MapQuantumDatabase --
//
// Our objective is to map all the information that the database is currently storing.  It
// may be necessary to do some hacking about with the database object in order to expose
// all the information necessary to accomplish this.
//
// Addition: 2/15/99: We want to print out statistics on the current state of the block
//  file, so let's do it here ( since we're traversing it to map the addresses, anyway ).
//////////////////////////////////////////////////////////////////////////////////////////////
void cDbDebug::MapQuantumDatabase(cDebugHierDb& db)
{
    //mirroring the implementation in blockrecordfile.h:
    typedef std::vector<cBlockRecordArray> BlockArray;

    //Using the two added methods to gain access to the low-level implementation of the
    //quantum database.
    BlockArray* pBlkArray = db.myGetBlockArray();

    // Print some statistics on the blockfile if we're in debug mode:
#ifdef _BLOCKFILE_DEBUG
    db.myGetBlockFile()->TraceContents();
#endif
    // Prints as much information about the current state of the block file as can be
    // assessed.

    // time to iterate over the database:
    std::vector<cBlockRecordArray>::iterator i;
    int                                      count = 0;

    for (i = (*pBlkArray).begin(); i != (*pBlkArray).end(); ++i, ++count)
    {
        util_InitBlockArray(*i);
        //This is necessary to make sure that each block is initialized as we iterate
        //over the block array.
        //
        // Print out statistics on this block, if we're in debug mode:
#ifdef _BLOCKFILE_DEBUG
        i->TraceContents();
#endif


        //Search through all the indexes and determine which of them references valid
        //information.  Store these <int, int> pairs ( the first int being the value of
        //count....
        for (int j = 0; j <= i->GetNumItems(); ++j)
        {
            if (i->IsItemValid(j))
            {
                //
                // We found a valid node in the database, so store it in the map.
                //
                mpData->mDbMap.insert(cDbDebug_i::hierDbMap::value_type(std::pair<int, int>(count, j), 0));
            }
            // if not, just don't store it.  The index is no longer valid.
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////
// TraverseHierarchy --
//
// A recursive function for traversing a hierarchical database, accounting for all the data
// structures that make up the object.  Looks at data addresses as well as structures that are
// necessary to maintain the hierarchy ( namely cHierArrayInfo records ).
////////////////////////////////////////////////////////////////////////////////////////////////
void cDbDebug::TraverseHierarchy(cDebugHierDbIter pIter)
{
    if (!pIter.CanDescend())
        return; //Done with this call.

    pIter.Descend();
    //
    //Descend once from the root, into the first child set. Also, descend once for each recursive call.
    //

    for (pIter.SeekBegin(); !pIter.Done(); pIter.Next())
    {
        //Try to match the parent's address in the database.
        if (!pIter.Done())
        {
            if (pIter.myGetEntryArrayIt() == pIter.myGetEntryArray().begin())
            //We're dealing with a cHierArrayInfo object, so treat it differently.
            {
                //
                // Let's map the parent address, and the array address, so we account for all the data structures
                // that make up this particular node.
                //
                MapHierDbNodes(mpData->mDbMap,
                               std::pair<int, int>(pIter.myGetArrayInfo().mParent.mBlockNum,
                                                   pIter.myGetArrayInfo().mParent.mIndex),
                               pIter);
                // and the array...
                MapHierDbNodes(
                    mpData->mDbMap,
                    std::pair<int, int>(pIter.myGetArrayInfo().mArray.mBlockNum, pIter.myGetArrayInfo().mArray.mIndex),
                    pIter);
            }
            else
            //This is a regular cHierEntry, so look at the Data and Child (if it exists ).
            {

                cDebugHierDbIter::EntryArray::iterator lEntryArrayIt = pIter.myGetEntryArrayIt();

                MapHierDbNodes(mpData->mDbMap,
                               std::pair<int, int>(lEntryArrayIt->mData.mBlockNum, lEntryArrayIt->mData.mIndex),
                               pIter);

                --lEntryArrayIt;
                // Get the address of this node by examining the previous next pointer in the entry list.
                //

                //
                // Map the next peer entry in the list. TODO: This may very well be overkill... if so, lose this call to MapHierDbNodes.
                //
                MapHierDbNodes(mpData->mDbMap,
                               std::pair<int, int>(lEntryArrayIt->mNext.mBlockNum, lEntryArrayIt->mNext.mIndex),
                               pIter);
                //
                // We also want to map the address of the child array, if non-null.
                //
                if (lEntryArrayIt->mChild.mBlockNum != -1)
                    MapHierDbNodes(mpData->mDbMap,
                                   std::pair<int, int>(lEntryArrayIt->mChild.mBlockNum, lEntryArrayIt->mChild.mIndex),
                                   pIter);
                //
                // Finally, map the address of the data.
                //
                MapHierDbNodes(mpData->mDbMap,
                               std::pair<int, int>(lEntryArrayIt->mData.mBlockNum, lEntryArrayIt->mData.mIndex),
                               pIter);
            }

        } //if
        //
        // Check to see if this particular node has a child array. If so, make recursive call.
        //
        if (pIter.CanDescend())
        {
            //This node has children, call myself
            TraverseHierarchy(pIter);
        }
    } //for
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MapHierDbNodes --
//
// Maps a given address in the hierarchy.  This function looks up the address in the map and flips an integer flag
// to record the event.  If the address is not found, it is placed in a list of addresses for error output.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void cDbDebug::MapHierDbNodes(std::map<std::pair<int, int>, int>& dbMap,
                              std::pair<int, int>                 address,
                              cDebugHierDbIter&                   pIter)
{
    cDbDebug_i::hierDbMap::iterator i = dbMap.find(std::pair<int, int>(address.first, address.second));

    if (i != dbMap.end())
    {
        (*i).second = 1;
        //Flip the integer flag to 1, since we've found the address in the hierarchy.
    }
    else
        mpData->mlMissingFromBlockFile.push_back(std::pair<int, int>(address.first, address.second));
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CongruencyTest --
//
// Iterate over the map and see if there are any entries that haven't been tagged by our
// hierarchy traversal.  If there are, push the offending addresses onto the address list.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void cDbDebug::CongruencyTest(void)
{
    cDbDebug_i::hierDbMap::iterator i = mpData->mDbMap.begin();
    for (; i != mpData->mDbMap.end(); ++i)
    {
        if ((*i).second != 1)
        {
            //We've found something in the blockfile map that is not accounted for by the
            //hierarchy.  Add this address to the appropriate list.
            //TCOUT<< (*i).first.first << _T(",") << (*i).first.second << std::endl;
            //TCOUT<< (*i).second <<std::endl;
            mpData->mlMissingFromHierarchy.push_back(std::pair<int, int>((*i).first.first, (*i).first.second));
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// OutputResults --
//
// Output the results of the congruency test.  Print information contained in the two
// "error lists" if they have entries.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void cDbDebug::OutputResults(void)
{
    TCOUT << _T("\nResults of Blockfile / Hierarchy congruency tests: \n") << std::endl;

    // Check to see if our error lists are empty.  If so, output a message that indicates
    // that the two databases checked clean.
    if (mpData->mlMissingFromHierarchy.empty() && mpData->mlMissingFromBlockFile.empty())
        TCOUT << _T("The database representations match!\n") << std::endl;
    else
    {
        // Check to see if there was anything in the hierarchy that was not accounted for by the
        // blockfile (quantum database).
        if (!mpData->mlMissingFromBlockFile.empty())
        {
            TCOUT << _T(" Objects (referenced by address) found in the hierarchy that were not\n");
            TCOUT << _T(" accounted for by the underlying quantum database: \n");

            cDbDebug_i::AddressList::iterator i = mpData->mlMissingFromBlockFile.begin();
            for (; i != mpData->mlMissingFromBlockFile.end(); ++i)
                TCOUT << _T("(") << (*i).first << _T(",") << (*i).second << _T(")") << std::endl;
        }
        else
            TCOUT << _T(" All objects in Blockfile accounted for!\n");

        //check to see if there was anything in the blockfile that was not accounted for by the
        // hierarchy:
        if (!mpData->mlMissingFromHierarchy.empty())
        {
            TCOUT << _T(" Database information (referenced by a <blocknumber, offset> address)\n");
            TCOUT << _T(" not accounted for by the hierarchy :\n");

            cDbDebug_i::AddressList::iterator i = mpData->mlMissingFromHierarchy.begin();
            for (; i != mpData->mlMissingFromHierarchy.end(); ++i)
                TCOUT << _T("(") << (*i).first << _T(",") << (*i).second << _T(")") << std::endl;
        }
        else
            TCOUT << _T("All objects in Hierarchy accounted for!\n");
    } //else
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Begin code for manipulating Database -- This is just a straight port from the DbExplore code.  It's presence is
// purely for convenience, and for debugging this class.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
static inline bool IsSpace( TCHAR c )
{
    return ( (c == _T(' ')) || (c == _T('\t')) || (c == _T('\r')) || (c == _T('\n')) || (c == _T('\0')) );
}

static inline bool IsEnd( TCHAR c )
{
    return ( (c == _T('\0')) );
}

///////////////////////////////////////////////////////////////////////////////
// util_PrintFCO
///////////////////////////////////////////////////////////////////////////////
static void util_PrintFCO( const iFCO* pFCO, const iFCOPropDisplayer* pDisplayer )
{
    TCOUT.setf(std::ios::left);
    
    TCOUT   << "------- " 
            << iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( pFCO->GetName() )
            << " -------" 
            << std::endl;
    //
    // iterate over all of the properties
    //
    const iFCOPropSet* pPropSet = pFCO->GetPropSet();
    cFCOPropVector v        = pPropSet->GetValidVector();
    for( int i=0; i < pPropSet->GetNumProps(); i++ )
    {   
        if( v.ContainsItem( i ) )
        {
            TCOUT << "[";
            TCOUT.width(2);
            TCOUT << i << "]" ;
            TCOUT.width(25);
            TCOUT << pPropSet->GetPropName(i);
            TCOUT.width(0);
            TCOUT << pDisplayer->PropAsString( pFCO, i ) << std::endl;
        }
    }
    TCOUT << "--------------------------------------------" << std::endl;
}

///////////////////////////////////////////////////////////////////////////////
// GetNoun
//      note -- you can only call this once after the verb (it eats the rest
//          of the line!)
///////////////////////////////////////////////////////////////////////////////
static void GetNoun( TSTRING& noun )
{
    static TSTRING prevNoun;
    TCHAR buf[1024];

    TCIN.getline( buf, 1024 );
    //
    // find the end of the noun...
    //
    TCHAR* pStart   = buf;
    TCHAR* end      = &buf[1023];
    while( IsSpace(*pStart) && (! IsEnd(*pStart)) && (pStart < end) )
        pStart++;

    if( IsEnd( *pStart ) || (pStart >= end) )
    {
        // no noun!
        noun = _T("");
        return;
    }
    TCHAR* pCur     = pStart;
    bool bQuote = false;
    if( *pCur == _T('\"') )
    {
        bQuote = true;
        pCur++;
        pStart++;
    }
    while( pCur < end )
    {
        if( (! bQuote) && IsSpace(*pCur) )
            break;

        if( *pCur == _T('\"') && bQuote)
            break;

        pCur++;
    }
    noun.assign(pStart, (pCur - pStart));

    if( noun.compare( _T("!$") ) == 0 )
    {
        noun = prevNoun;
    }
    prevNoun = noun;
}

///////////////////////////////////////////////////////////////////////////////
// SeekTo -- seeks to the named noun; this takes care of interpriting special 
//      nouns. If the seek fails, then Done() is true and false is returned.
//      If noun is a special character, then it is altered to what it mapped to.
///////////////////////////////////////////////////////////////////////////////
static bool SeekTo(cDbDataSourceIter* pIter, TSTRING& noun )
{
    if( noun[0] == _T('*') )
    {
        // interprite the rest of the string as a number index to seek to...
        int index = _ttoi( &noun[1] );
        if( index < 0 )
            return false;
        pIter->SeekBegin();
        for( int i=0; ((i < index) && (! pIter->Done())); i++, pIter->Next() )
        {

        }
        if( pIter->Done() )
            return false;
        noun = pIter->GetShortName();
        return true;
    }
    else
        return ( pIter->SeekTo( noun.c_str() ) );
}

void cDbDebug::ManipDb( cFCODatabaseFileIter& dbIter )
{
    cDbDataSourceIter* pIter        = new cDbDataSourceIter( &dbIter.GetDb(), dbIter.GetGenre() );
    const iFCOPropDisplayer* pDisplayer = dbIter.GetGenreHeader().GetPropDisplayer();

    ////////////////////////////
    // the main event loop...
    ////////////////////////////
    while( true )
    {
        TSTRING verb, noun;
        TCOUT << _T(">>");
        TCIN >> verb;
        //
        // ok, now we switch on the command...
        //
        //-----------------------------------------------------------------
        // quit
        //-----------------------------------------------------------------
        if( verb.compare( _T("quit") ) == 0 )
        {
            // the quit command...
            break;
        }
        //-----------------------------------------------------------------
        // print
        //-----------------------------------------------------------------
        if( verb.compare( _T("print") ) == 0 )
        {
            GetNoun(noun);
            if( SeekTo( pIter, noun ) )
            {
                if( pIter->HasFCOData() )
                {
                    iFCO* pFCO = pIter->CreateFCO();
                    util_PrintFCO( pFCO, pDisplayer );
                    pFCO->Release();
                }
                else
                {
                    TCOUT << "Object has no data associated with it." << std::endl;
                }
            }
            else
            {
                TCOUT << "Unable to find object " << noun << std::endl;
            }
        }
        //-----------------------------------------------------------------
        // pwd
        //-----------------------------------------------------------------
        else if( verb.compare( _T("pwd") ) == 0 )
        {
            TCOUT << iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( pIter->GetParentName() ) << std::endl;
        }
        //-----------------------------------------------------------------
        // ls
        //-----------------------------------------------------------------
        else if( verb.compare( _T("ls") ) == 0 )
        {
            int cnt = 0;
            for( pIter->SeekBegin(); ! pIter->Done(); pIter->Next(), cnt++ )
            {
                TCOUT << "[" << cnt ;
                if( pIter->CanDescend() )
                {
                    TCOUT << "]*\t" ;
                }
                else
                {
                    TCOUT << "]\t" ;
                }
                TCOUT << pIter->GetShortName() << std::endl;
            }
        }
        //-----------------------------------------------------------------
        // cd
        //-----------------------------------------------------------------
        else if( verb.compare( _T("cd") ) == 0 )
        {
            GetNoun(noun);
            if( noun.compare( _T("..") ) == 0 )
            {
                if( pIter->AtRoot() )
                {
                    TCOUT << "Can't ascend above root." << std::endl;
                }
                else
                {
                    TCOUT << "Ascending..." << std::endl;
                    pIter->Ascend();
                }
            }
            else
            {
                if( SeekTo( pIter, noun ) )
                {
                    if( pIter->CanDescend() )
                    {
                        TCOUT << "Descending into " << noun << std::endl;
                        pIter->Descend();
                    }
                    else
                    {
                        TCOUT << noun << " has no children; can't descend." << std::endl;
                    }
                }
                else
                {
                    TCOUT << "Unable to find object " << noun << std::endl;
                }
            }
        }
        //-----------------------------------------------------------------
        // cg
        //-----------------------------------------------------------------
        else if( verb.compare( _T("cg") ) == 0 )
        {
            GetNoun(noun);

            cGenre::Genre newGenre = cGenreSwitcher::GetInstance()->StringToGenre( noun.c_str() );

            if (newGenre != cGenre::GENRE_INVALID)
            {
                dbIter.SeekToGenre( newGenre );
                if( !dbIter.Done() )
                {
                    TCOUT << _T("Changing to Genre ") << noun << std::endl;
                    //
                    // create a new db iter for the new genre (db iters can only be
                    // assocaited with a single genre :-( )
                    //
                    delete pIter;
                    pIter = new cDbDataSourceIter( &dbIter.GetDb(), newGenre );
                }
                else
                {
                    TCOUT << _T("Unable to find Genre ") << noun << std::endl;
                }
            }
            else
            {
                TCOUT << _T("Invalid Genre ") << noun << std::endl;
            }
        }
        //-----------------------------------------------------------------
        // pwg
        //-----------------------------------------------------------------
        else if( verb.compare( _T("pwg") ) == 0 )
        {
            TCOUT << _T("Current Genre: ") << cGenreSwitcher::GetInstance()->GenreToString( (cGenre::Genre)dbIter.GetGenre(), true ) << std::endl;
        }

        //-----------------------------------------------------------------
        // mkdir
        //-----------------------------------------------------------------
        else if( verb.compare( _T("mkdir") ) == 0 )
        {
            GetNoun(noun);
            TCOUT << "Making a child of " << noun << std::endl;
            if( pIter->SeekTo( noun.c_str() ) )
            {
                pIter->AddChildArray();
            }
            else
            {
                TCOUT << "Unable to find object " << noun << std::endl;
            }
        }
        //-----------------------------------------------------------------
        // mk
        //-----------------------------------------------------------------
        else if( verb.compare( _T("mk") ) == 0 )
        {
            GetNoun(noun);
            TCOUT << "Making object " << noun << std::endl;
            if( pIter->SeekTo( noun.c_str() ) )
            {
                TCOUT << "Error: object already exists!" << std::endl;
            }
            else
            {
                pIter->AddFCO( noun, 0 );   // add a null fco for now
            }
        }
        //-----------------------------------------------------------------
        // rmdir
        //-----------------------------------------------------------------
        // TODO -- still needs to be implemented in the pIterator class!
        //
        else if( verb.compare( _T("rmdir") ) == 0 )
        {
            GetNoun(noun);
            TCOUT << "Removing the child of " << noun << std::endl;
            if( pIter->SeekTo( noun.c_str() ) )
            {
                //TODO -- check that it has an empty child
                pIter->RemoveChildArray();
            }
            else
            {
                TCOUT << "Unable to find object " << noun << std::endl;
            }
        }
        //-----------------------------------------------------------------
        // rm
        //-----------------------------------------------------------------
        else if( verb.compare( _T("rm") ) == 0 )
        {
            GetNoun(noun);
            TCOUT << "Removing object " << noun << std::endl;
            if( pIter->SeekTo( noun.c_str() ) )
            {
                if( pIter->CanDescend() )
                {
                    TCOUT << "Can't delete object; it still has children." << std::endl;
                }
                else
                {
                    pIter->RemoveFCO();
                }
            }
            else
            {
                TCOUT << "Unable to find object " << noun << std::endl;
            }
        }
        //-----------------------------------------------------------------
        // help
        //-----------------------------------------------------------------
        if( verb.compare( _T("help") ) == 0 )
        {
            TCOUT   << _T("Commands: ")             << std::endl
                    << _T(" cd <dir_name>")         << std::endl
                    << _T(" pwd ")                  << std::endl
                    << _T(" ls ")                   << std::endl
                    << _T(" print <object_name>")   << std::endl
                    << _T(" cg (FS | NTFS | NTREG)")<< std::endl
                    << _T(" pwg")                   << std::endl
                    << _T(" quit")                  << std::endl;
        }

        // make sure the file is still valid...
        //
    }

    delete pIter;
    TCOUT << "Exiting..." << std::endl;
}
*/
