//-----------------------------------------------------------------------------
// File: sample.cpp
// Title: Simple application template
//-----------------------------------------------------------------------------

#define PCOM_INIT_GUID


#include <psareg.h>
#include <psacom.h>
#include <stdio.h>
#include "xflaim.h"

#ifdef UNIX
    #include <sys/utsname.h>
#endif

FLMBOOL gv_bShutdown = FALSE;

#if defined(NLM)

    #define DB_NAME_STR ((FLMBYTE *)"SYS:\\TST.DB")
    #define BACKUP_NAME_STR ((FLMBYTE *)"SYS:\\TST.BAK")
    #define NEW_NAME_STR ((FLMBYTE *)"SYS:\\NEW.DB")

    FLMBOOL gv_bRunning = FALSE;

    extern "C"
    {
        int nlm_main( int            iArgC,
                             char **    ppucArgV);

        void ConsolePrintf( const char *format, ...);
        void SynchronizeStart( void);
        void sampleCleanup( void);
    }

    void sampleCleanup( void)
    {
        while( gv_bRunning)
        {
            f_yieldCPU();
        }
    }

#else

    #define DB_NAME_STR "tst.db"
    #define BACKUP_NAME_STR "tst.bak"
    #define NEW_NAME_STR "new.db"

#endif

#define MAX_KEY_SIZ 1024

// Update this code to point to where the .dll files are located.
#ifdef UNIX
    const char * pszComServerPaths[] = { ".", "./tests", "../lib", 0};
#else
    const char * pszComServerPaths[] = { ".", 0 };
#endif

#define PCOM_INIT_GUID
// {B68FE080-AB53-400d-89AF-DAB2FF592F77}
PCOM_DEFINE_GUID(CLSID_XFlaim_Sample_Prog,
                                         0xb68fe080, 0xab53, 0x400d, 0x89,
                                             0xaf, 0xda, 0xb2, 0xff, 0x59, 0x2f, 0x77);

void printMessage(
    char *            pszMessage,
    FLMUINT    uiLevel = 0);

RCODE printDocument(
    IF_Db *                 pDb,
    IF_DOMNode *    pRootNode);

RCODE processAttributes(
    IF_Db *                 pDb,
    IF_DOMNode *    pNode,
    FLMBYTE **        ppszLine);

/***************************************************************************
Desc: Program entry point (main)
****************************************************************************/
#if defined( UNIX)
int main(
    int,             // iArgC,
    char **      // ppucArgV
    )
#elif defined( NLM)
int nlm_main(
    int,             // iArgC,
    char **      // ppucArgV
    )
#else
int __cdecl main(
    int,             // iArgC,
    char **      // ppucArgV
    )
#endif
{
    RCODE                         rc = NE_XFLM_OK;
    FLMBYTE                    ucMsgBuf[ 200];
    XFLM_CREATE_OPTS       createOpts;
    IF_Db *                         pDb = NULL;
    IF_Backup *                  pBackup = NULL;
    IF_DOMNode *            pTmpNode = NULL;
    bool                                bPsaInitialized = false;
    IF_DbSystem *               pDbSystem = NULL;
    int                                    iErr = 0;
    FLMUINT                      uiMusicElementDef=0;
    FLMUINT                      uiCdElementDef=0;
    FLMUINT                      uiTitleElementDef=0;
    FLMUINT                      uiArtistElementDef=0;
    FLMUINT                      uiTracksElementDef=0;
    FLMUINT                      uiNoAttrDef=0;
    FLMUINT                      uiTitleAttrDef=0;
    FLMUINT                      uiCollectionDef=0;
    FLMBOOL                    bTranStarted = FALSE;
    IF_DOMNode *            pCdChild = NULL;
    IF_DOMNode *            pCdElement = NULL;
    IF_DOMNode *            pMusicElement = NULL;
    IF_DOMNode *            pCdAttr = NULL;
    IF_DOMNode *            pTrackNode = NULL;
    IF_Query *                    pQuery = NULL;
    IF_PosIStream *            pPosIStream = NULL;
    IF_PosIStream *            pIStream = NULL;
    FLMUINT                     uiIndex = 1;
    FLMBYTE                    ucUntilKeyBuf[ MAX_KEY_SIZ];
    FLMUINT                    uiUntilKeyLen;
    FLMBYTE                    ucCurrentKeyBuf[ MAX_KEY_SIZ];
    FLMUINT                    uiCurrentKeyLen;
    FLMUINT64                ui64DocId;
    char                               ucTitle[ 200];
    FLMUINT                    uiTitleLen;
    char * ppszPath[] =
        {
                ".\\xmlfiles\\7001e10c",
                ".\\xmlfiles\\70028663",
                ".\\xmlfiles\\70037c08",
                ".\\xmlfiles\\70040b08",
                ".\\xmlfiles\\70044808",
                ".\\xmlfiles\\70045109",
                ".\\xmlfiles\\70045e09",
                ".\\xmlfiles\\70046709",
                ".\\xmlfiles\\7004920a",
                ""
        };
    FLMUINT                     uiLoop;
    IF_DataVector *            pFromKeyV = NULL;
    IF_DataVector *            pUntilKeyV = NULL;
    char                                szQueryString[] = "/music/cd/tracks[@title~=\"we our in luv\"]";
    char *                             pszIndex =
                "<xflaim:Index "
                "xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" "
                "xmlns:xflaim=\"http://www.novell.com/XMLDatabase/Schema\" "
                "xs:name=\"title_IX\">"
                "<xflaim:ElementComponent "
                "xs:name=\"title\" "
                "xflaim:DictNum=\"1\" "
                "xflaim:IndexOn=\"value\" "
                "xflaim:KeyComponent=\"1\"/>"
                "</xflaim:Index>"
;


#ifdef NLM

    gv_bRunning = TRUE;

    // Setup the routines to be called when the NLM exits

    atexit( sampleCleanup);
    SynchronizeStart();

#endif

    // Initialize the PSA subsystem
    iErr = PsaComInitialize( CLSID_XFlaim_Sample_Prog, pszComServerPaths);
    if (iErr)
    {
        printf( "\nERROR: Unable to initialize PSA");
        goto Exit;
    }
    else
    {
        bPsaInitialized = true;
    }


    // Create the database system object that we will use to access the XFlaim
    // database API and initialize it for use.

    if (RC_BAD( rc = PsaComCreateInstance( CLSID_F_DbSystemFactory,

                                                                       NULL,
                                                                       IID_IF_DbSystem,
                                                                       (void **)&pDbSystem)))
    {
        goto Exit;
     }

    if ( RC_BAD( rc = pDbSystem->init()))
    {
        goto Exit;
    }

    // Create the database. This code will remove the database first if it
    // exists. Once removed, it will try to create it. If that fails for
    // any reason other than the database already exists, it will exit.

RetryCreate:

    memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS));

    if ( RC_BAD( rc = pDbSystem->dbCreate( DB_NAME_STR,
                                                                        NULL,
                                                                        NULL,
                                                                        NULL,
                                                                        NULL,
                                                                        &createOpts,
                                                                        &pDb)))
    {
        if ( RC_BAD( rc == NE_XFLM_FILE_EXISTS))
        {
            if ( RC_BAD( rc = pDbSystem->dbRemove( DB_NAME_STR,
                                                                                  NULL,
                                                                                  NULL,
                                                                                  TRUE)))
            {
                goto Exit;
            }

            goto RetryCreate;
        }
        else
        {
            goto Exit;
        }
    }

    // Start an update transaction. All access to the database should
    // be done within the confines of a transaction. When changed are being
    // made to the database, an UPDATE transaction is required. It is best to
    // keep transactions small if possible.


    if ( RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS, 0)))
    {
        goto Exit;
    }
    bTranStarted = TRUE;

/*
Let's create a document to demonstrate how we use the DOM...
Our document will catalog a music CD. It will look like:

<music>
    <cd>
        <title>We Are In Love</title>
         <artist>Harry Connick Jr.</artist>
        <tracks no="1" title="We Are In Love"/>
        <tracks no="2" title="Only 'Cause I Don't Have You"/>
    </cd>
</music>

To accomplish this, we must define the elements of the document and a new
collection to store the document in.
*/


    // Create some element definitions for our document.
    // Create the "music" element definition.


    if ( RC_BAD( rc = pDb->createElementDef( NULL,
                                                                         "music",
                                                                         XFLM_NODATA_TYPE,
                                                                         &uiMusicElementDef)))
    {
        goto Exit;
    }

    // Create the "cd" element definition.

    if ( RC_BAD( rc = pDb->createElementDef( NULL,
                                                                         "cd",
                                                                         XFLM_NODATA_TYPE,
                                                                         &uiCdElementDef)))
    {
        goto Exit;
    }

    // Create the "title" element definition.

    if ( RC_BAD( rc = pDb->createElementDef( NULL,
                                                                         "title",
                                                                         XFLM_TEXT_TYPE,
                                                                         &uiTitleElementDef)))
    {
        goto Exit;
    }

    // Create the "artist" element definition.

    if ( RC_BAD( rc = pDb->createElementDef( NULL,
                                                                         "artist",
                                                                         XFLM_TEXT_TYPE,
                                                                         &uiArtistElementDef)))
    {
        goto Exit;
    }

    // Create the "tracks" element definition.

    if ( RC_BAD( rc = pDb->createElementDef( NULL,
                                                                         "tracks",
                                                                         XFLM_NODATA_TYPE,
                                                                         &uiTracksElementDef)))
    {
        goto Exit;
    }

    // Create the "no" attribute definition.

    if ( RC_BAD( rc = pDb->createAttributeDef( NULL,
                                                                          "no",
                                                                          XFLM_NUMBER_TYPE,
                                                                         &uiNoAttrDef)))
    {
        goto Exit;
    }

    // Create the "title" attribute definition.

    if ( RC_BAD( rc = pDb->createAttributeDef( NULL,
                                                                          "title",
                                                                          XFLM_TEXT_TYPE,
                                                                          &uiTitleAttrDef)))
    {
        goto Exit;
    }

    // Create our special music collection for storing music stuff.

    if ( RC_BAD( rc = pDb->createCollectionDef( "Music Collection",
                                                                            &uiCollectionDef)))
    {
        goto Exit;
    }


    // We now have all of the definitions we need to build our document.
    // Lets first create a new document, followed by its members...


    // Create the "music" root element
    if ( RC_BAD( rc = pDb->createRootElement( uiCollectionDef,
                                                                           uiMusicElementDef,
                                                                           &pMusicElement)))
    {
        goto Exit;
    }

    // Create the "cd" element

    if ( RC_BAD( rc = pMusicElement->createNode( pDb,
                                                                                 ELEMENT_NODE,
                                                                                 uiCdElementDef,
                                                                                 XFLM_FIRST_CHILD,
                                                                                 &pCdElement)))
    {
        goto Exit;
    }


    // Create the "title" element

    if ( RC_BAD( rc = pCdElement->createNode( pDb,
                                                                            ELEMENT_NODE,
                                                                            uiTitleElementDef,
                                                                            XFLM_FIRST_CHILD,
                                                                            &pCdChild)))
    {
        goto Exit;
    }


    // Set the value for the title.

    if (RC_BAD( rc = pCdChild->setNative( pDb, "We Are In Love")))
    {
        goto Exit;
    }

    // Create the "artist" element

    if ( RC_BAD( rc = pCdElement->createNode( pDb,
                                                                            ELEMENT_NODE,
                                                                            uiArtistElementDef,
                                                                            XFLM_LAST_CHILD,
                                                                            &pCdChild)))
    {
        goto Exit;
    }


    // Set the value for the title.

    if (RC_BAD( rc = pCdChild->setNative( pDb, "Harry Connick Jr.")))
    {
        goto Exit;
    }

    // Create the first "tracks" element

    if ( RC_BAD( rc = pCdElement->createNode( pDb,
                                                                            ELEMENT_NODE,
                                                                            uiTracksElementDef,
                                                                            XFLM_LAST_CHILD,
                                                                            &pCdChild)))
    {
        goto Exit;
    }

    // Create the "no." attribute, then the "title" attribute.
    if (RC_BAD( rc = pCdChild->createAttribute( pDb,
                                                                            uiNoAttrDef,
                                                                            &pCdAttr)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pCdAttr->setUINT( pDb, (FLMUINT)1)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pCdChild->createAttribute( pDb,
                                                                            uiTitleAttrDef,
                                                                            &pCdAttr)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pCdAttr->setNative( pDb,
                                                                 "We Are In Love")))
    {
        goto Exit;
    }

    // Create the next "tracks" element

    if ( RC_BAD( rc = pCdElement->createNode( pDb,
                                                                            ELEMENT_NODE,
                                                                            uiTracksElementDef,
                                                                            XFLM_LAST_CHILD,
                                                                            &pCdChild)))
    {
        goto Exit;
    }

    // An alternate way to create the attributes and set their values is to use
    // the parent element to set the attribute values.


    // Create the two attributes.

    if (RC_BAD( rc = pCdChild->createAttribute( pDb,
                                                                            uiNoAttrDef,
                                                                            &pCdAttr)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pCdChild->createAttribute( pDb,
                                                                            uiTitleAttrDef,
                                                                            &pCdAttr)))
    {
        goto Exit;
    }

    // Using the parent of the attributes, set their values by specifying
    // which attribute to set.

    if (RC_BAD( rc = pCdChild->setAttributeValueUINT( pDb,
                                                                                         uiNoAttrDef,
                                                                                         (FLMUINT)2)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pCdChild->setAttributeValueNative( pDb,
                                                                                          uiTitleAttrDef,
                                                                                          "Only 'Cause I Don't Have You")))
    {
        goto Exit;
    }

    // It is always good practice to call documentDone whenever updates to a
    // document are completed.

    if (RC_BAD( rc = pDb->documentDone( uiCollectionDef,
                                                                     pMusicElement->getId())))
    {
        goto Exit;
    }


    // Commit the transaction

    if ( RC_BAD( rc = pDb->transCommit()))
    {
        goto Exit;
    }
    bTranStarted= FALSE;

    // Now we want to read back our document, and display it for all
    // the world to see...:^)


    // Start a read transaction

    if ( RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS, 0)))
    {
        goto Exit;
    }
    bTranStarted = TRUE;

    // Read the nodes. Start with the document node.

    if ( RC_BAD( rc = pDb->getFirstDocument( uiCollectionDef, &pTmpNode)))
    {
        goto Exit;
    }

    // printDocument is a simple function that walks through the document
    // and displays it, node by node as an XML document. It assumes the
    // node passed in is the root node. If it is not, then the display will
    // look a bit odd, as you will see later in this sample program.

   
    if (RC_BAD( rc = printDocument( pDb, pTmpNode)))
    {
        goto Exit;
    }

    // Commit the transaction

    if ( RC_BAD( rc = pDb->transCommit()))
    {
        goto Exit;
    }
    bTranStarted = FALSE;

    // Start an update transaction

    if ( RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS, 0)))
    {
        goto Exit;
    }
    bTranStarted = TRUE;

    // Let's do a query on this document... Our query (above) is
    // deliberately miss-spelling some of the words. It is looking for
    // the music "tracks" with the title "We Are In Love".
    // The syntax ~= means approximately equals, and is a "sounds like"
    // search.


    if (RC_BAD( rc = pDbSystem->createIFQuery( &pQuery)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pQuery->setCollection( uiCollectionDef)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pQuery->setupQueryExpr( pDb, szQueryString)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pQuery->getFirst( pDb, &pTrackNode)))
    {
        goto Exit;
    }
 

    // Show the results...
    printMessage( "Query:");
    printMessage( (char *)szQueryString);

    // This will print the tracks node, followed by all of the closing parent
    // nodes.

    if (RC_BAD( rc = printDocument( pDb, pTrackNode)))
    {
        goto Exit;
    }
 

    // Always cleanup...
    pQuery->Release();
    pQuery = NULL;

    // Commit the transaction

    if ( RC_BAD( rc = pDb->transCommit()))
    {
        goto Exit;
    }
    bTranStarted = FALSE;

    // Let's create an index to make searching easier. An index is essentially
    // just another XML document to XFlaim, however it is created in the dictionary
    // collection rather than in a data collection. There are two ways to create
    // a document in XFlaim. One way (demonstrated above) is to create each node
    // of the document, one at a time. The other is to import the document.
    // Creating the index will demonstrate importing the document. Our
    // index definition is shown as follows:
    //
/*
<xflaim:Index
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xflaim="http://www.novell.com/XMLDatabase/Schema"
    xs:name="title_IX">
    <xflaim:ElementComponent
        xs:name="title"
        xflaim:DictNum="1"
        xflaim:IndexOn="value"/>
</xflaim:Index>"
*/
    // For our purposes, we will create a local variable (pszIndex) which
    // holds the index document. We will import that using the importDocument
    // method of the pDb object.

    // Import the index...
    // We first need to create a BufferIStream object to stream the document
    // from...
    // Start an update transaction


    if ( RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS, 0)))
    {
        goto Exit;
    }
    bTranStarted = TRUE;

    if ( RC_BAD( rc = pDbSystem->openBufferIStream(
                                                            pszIndex,
                                                            strlen( pszIndex),
                                                            &pPosIStream)))
    {
        goto Exit;
    }

    if ( RC_BAD( rc = pDb->import(
                            pPosIStream,
                            XFLM_DICT_COLLECTION)))
    {
        goto Exit;
    }

    // Commit the transaction

    if ( RC_BAD( rc = pDb->transCommit()))
    {
        goto Exit;
    }
    bTranStarted = FALSE;

    // Now, let's get some additional documents in so we can do some more
    // interesting stuff using a IF_DataVector. The documents will be imported
    // from the xmlfiles directory. The documents we are interested in searching
    // are the CD music documents. They have the following format:


/*

<?xml version="1.0"?>
<disc>
    <id>00097210</id>
    <length>2420</length>
    <title>Frank Sinatra / Blue skies</title>
    <genre>cddb/jazz</genre>
    <track index="1" offset="150">blue skies</track>
    .
    .
    .
</disc>

*/


    if ( RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS, 0)))
    {
        goto Exit;
    }
    bTranStarted = TRUE;

    // We will first need an input file stream.
    uiLoop = 0;
    while (strlen( ppszPath[ uiLoop]))
    {
        if (RC_BAD( rc = pDbSystem->openFileIStream( ppszPath[ uiLoop], &pIStream)))
        {
            goto Exit;
        }
        if (RC_BAD( rc = pDb->import(
                                                    pIStream,
                                                    XFLM_DATA_COLLECTION)))
        {
            goto Exit;
        }
        uiLoop++;
    }

    // Commit the transaction

    if ( RC_BAD( rc = pDb->transCommit()))
    {
        goto Exit;
    }
    bTranStarted = FALSE;

    // Let's get some IF_DataVectors to work with.

    if ( RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS, 0)))
    {
        goto Exit;
    }
    bTranStarted = TRUE;

    if (RC_BAD( rc = pDbSystem->createIFDataVector( &pFromKeyV)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pDbSystem->createIFDataVector( &pUntilKeyV)))
    {
        goto Exit;
    }

    // We need to get the index.

    // Now to search the index above for all document titles in the index and
    // display the document Id for each node and the title. Note that the index
    // number is known to be 1.


    if (RC_BAD( rc = pDb->keyRetrieve( uiIndex, NULL, XFLM_FIRST, pFromKeyV)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pDb->keyRetrieve( uiIndex, NULL, XFLM_LAST, pUntilKeyV)))
    {
        goto Exit;
    }

    // Save the UntilKey value to compare with later.

    if (RC_BAD( rc = pUntilKeyV->outputKey(
                                                        pDb,
                                                        uiIndex,
                                                        FALSE,
                                                        &ucUntilKeyBuf[0],
                                                        MAX_KEY_SIZ,
                                                        &uiUntilKeyLen)))
    {
        goto Exit;
    }

    for (;;)
    {
        // Display the current Document Id and the title.
        ui64DocId = pFromKeyV->getDocumentID();

        uiTitleLen = sizeof(ucTitle);
        if (RC_BAD( rc = pFromKeyV->getNative( 0, &ucTitle[0], &uiTitleLen)))
        {
            goto Exit;
        }

        sprintf( (char *)&ucMsgBuf[0], "DocId: %I64u\n%s\n", ui64DocId, ucTitle);
        printMessage( (char *)&ucMsgBuf[0]);

        // Check to see if this key matches the last or UntilKeyV value.
        if (RC_BAD( rc = pFromKeyV->outputKey(
                                                            pDb,
                                                            uiIndex,
                                                            FALSE,
                                                            &ucCurrentKeyBuf[0],
                                                            MAX_KEY_SIZ,
                                                            &uiCurrentKeyLen)))
        {
            goto Exit;
        }

        if (uiCurrentKeyLen == uiUntilKeyLen)
        {
            if (memcmp( ucCurrentKeyBuf, ucUntilKeyBuf, uiCurrentKeyLen) == 0)
            {
                break; // We are done!
            }
        }
        // Get the next key.
        if (RC_BAD( rc = pDb->keyRetrieve( uiIndex, pFromKeyV, XFLM_EXCL, pFromKeyV)))
        {
            goto Exit;
        }
    }

    if( RC_BAD( rc = pDb->transCommit()))
    {
        goto Exit;
    }
    bTranStarted = FALSE;

    // Close the database
    pDb->Release();
    pDb = NULL;

    // Close all unused files

    if ( RC_BAD( rc = pDbSystem->closeUnusedFiles( 0)))
    {
        goto Exit;
    }

    // Re-open the database

    if ( RC_BAD( rc = pDbSystem->dbOpen( DB_NAME_STR,
                                                                      NULL,
                                                                      NULL,
                                                                      &pDb)))
    {
        goto Exit;
    }

    // Backup the database

    if ( RC_BAD( rc = pDb->backupBegin( XFLM_FULL_BACKUP,
                                                                  XFLM_READ_TRANS,
                                                                  0,
                                                                 &pBackup)))

    {
        goto Exit;
    }

    if ( RC_BAD( rc = pBackup->backup( BACKUP_NAME_STR,
                                                                 NULL,
                                                                 NULL,
                                                                 NULL)))
    {
        goto Exit;
    }

    if (RC_BAD( rc = pBackup->endBackup()))
    {
        goto Exit;
    }

    pBackup->Release();
    pBackup = NULL;

    // Close the database again

    pDb->Release();
    pDb = NULL;

    if ( RC_BAD( rc = pDbSystem->closeUnusedFiles( 0)))
    {
        goto Exit;
    }

    // Remove the database

    if ( RC_BAD( rc = pDbSystem->dbRemove( DB_NAME_STR,
                                                                          NULL,
                                                                          NULL,
                                                                          TRUE)))
    {
        goto Exit;
    }

    // Restore the database

    if ( RC_BAD( rc = pDbSystem->dbRestore( DB_NAME_STR,
                                                                         NULL,
                                                                         NULL,
                                                                         BACKUP_NAME_STR,
                                                                         NULL,
                                                                         NULL)))
    {
        goto Exit;
    }

    // Rename the database

    if ( RC_BAD( rc = pDbSystem->dbRename( DB_NAME_STR,
                                                                          NULL,
                                                                          NULL,
                                                                          NEW_NAME_STR,
                                                                          TRUE,
                                                                          NULL)))
    {
        goto Exit;
    }

    // Copy the database

    if ( RC_BAD( rc = pDbSystem->dbCopy( NEW_NAME_STR,
                                                                      NULL,
                                                                      NULL,
                                                                      DB_NAME_STR,
                                                                      NULL,
                                                                      NULL,
                                                                      NULL)))
    {
        goto Exit;
    }

    // Remove the new database

    if ( RC_BAD( rc = pDbSystem->dbRemove( NEW_NAME_STR,
                                                                          NULL,
                                                                          NULL,
                                                                          TRUE)))
    {
        goto Exit;
    }

Exit:

    if (bTranStarted && pDb)
    {
        (void)pDb->transAbort();
    }

    if (pCdChild)
    {
        pCdChild->Release();
    }

    if (pCdElement)
    {
        pCdElement->Release();
    }

    if ( pMusicElement)
    {
        pMusicElement->Release();
    }

    if (pCdAttr)
    {
        pCdAttr->Release();
    }

    if (pTrackNode)
    {
        pTrackNode->Release();
    }

    if ( pTmpNode)
    {
        pTmpNode->Release();
    }

    if (pQuery)
    {
        pQuery->Release();
    }

    if ( pPosIStream)
    {
        pPosIStream->Release();
    }

    if (pIStream)
    {
        pIStream->Release();
    }

    if (pFromKeyV)
    {
        pFromKeyV->Release();
    }

    if (pUntilKeyV)
    {
        pUntilKeyV->Release();
    }

    if ( pBackup)
    {
        pBackup->Release();
    }

    // Close the database object

    if ( pDb)
    {
        pDb->Release();
    }

    if ( RC_BAD( rc))
    {
        sprintf( (char *)ucMsgBuf, "Error %04X -- %s\n",
                   (unsigned)rc,
                   (char *)pDbSystem->errorString( rc));
        printMessage( (char *)ucMsgBuf);
    }

    // Shut down the XFlaim database engine. This call must be made
    // even if the init call failed. No more XFlaim calls should be made
    // by the application.


    pDbSystem->exit();
    pDbSystem->Release();

    return( 0);
    }

/***************************************************************************
Desc: Prints a string to stdout
****************************************************************************/

void printMessage(
    char *                 pszMessage,
    FLMUINT         uiLevel)
{
    unsigned int             uiLoop;

    for (uiLoop = 0; uiLoop < uiLevel; uiLoop++)
    {
#ifdef NLM
        ConsolePrintf( " ");
#else
        printf( " ");
#endif
    }

#ifdef NLM
    ConsolePrintf( pszMessage);
#else
    printf( "%s\n", pszMessage);
#endif
}

/*=============================================================================
Desc: Simple routine to display the contents of a document.
=============================================================================*/

RCODE printDocument(
    IF_Db *                 pDb,
    IF_DOMNode *    pRootNode
    )
{
    RCODE                     rc = NE_XFLM_OK;
    FLMBYTE                ucLine[ 200];
    FLMBYTE *             pszLine;
    FLMBOOL               bHasChildren = FALSE;
    FLMBOOL               bHasNextSibling = FALSE;
    FLMUINT                 uiNodeType;
    FLMBOOL               bHadAttributes;
    FLMUINT                 uiDataType;
    char                           szName[ 25];
    FLMUINT                uiNameLen;
    IF_DOMNode *       pNode = pRootNode;
    char                           szTemp[ 200];
    FLMUINT                uiTemp;
    FLMBOOL               bUnwinding = FALSE;
    FLMBOOL               bHadValue = FALSE;
    FLMUINT                uiThisLevel = 0;
    FLMUINT                uiNextLevel = 0;

    pNode->AddRef();

    pszLine = &ucLine[0];
    bHadAttributes = FALSE;

    while (pNode)
    {

        bHadValue = FALSE;

        if (pszLine != &ucLine[0])
        {
            printMessage( (char *)ucLine, uiThisLevel);
            pszLine = &ucLine[0];
        }

        uiThisLevel = uiNextLevel;

        uiNodeType = pNode->getNodeType();

        // Get the nameId for the node
        if (uiNodeType == DOCUMENT_NODE)
        {
            if (!bUnwinding)
            {
                sprintf( (char *)pszLine, "<doc");
            }
            else
            {
                sprintf( (char *)pszLine, "</doc>");
            }
            pszLine += strlen( (char *)pszLine);
        }
        else
        {
            if (RC_BAD( rc = pNode->getLocalName( pDb, szName, 50, &uiNameLen)))
            {
                goto Exit;
            }

            if (!bUnwinding)
            {
                sprintf( (char *)pszLine, "<%s", szName);
            }
            else
            {
                sprintf( (char *)pszLine, "</%s>", szName);
            }
            pszLine += strlen( (char *)pszLine);
        }

        // Not the nicest thing to do, but what can it works...
        if (bUnwinding)
        {
            goto GetParent;
        }

        if (RC_BAD( rc = processAttributes( pDb, pNode, &pszLine)))
        {
            goto Exit;
        }

        // Value check.
        if (RC_BAD( rc = pNode->getDataType( pDb, &uiDataType)))
        {
            goto Exit;
        }

        switch (uiDataType)
        {
            case XFLM_TEXT_TYPE:
            {
                bHadValue = TRUE;
                if (RC_BAD( rc = pNode->getNative( pDb, szTemp, 0, 200, &uiTemp)))
                {
                    goto Exit;
                }
                sprintf( (char *)pszLine, ">%s", szTemp);
                pszLine += (uiTemp + 1);
                break;
            }
            case XFLM_NUMBER_TYPE:
            {
                bHadValue = TRUE;
                if (RC_BAD( rc = pNode->getUINT( pDb, &uiTemp)))
                {
                    goto Exit;
                }
                sprintf( (char *)szTemp, "%u", uiTemp);
                sprintf( (char *)pszLine, ">%s", szTemp);
                pszLine += (strlen( (char *)szTemp) + 1);
                break;
            }
            // Could also get binary data, but we won't handle it here.
        }

        // We need to know if this node has children to know how to close the line.
        if (RC_BAD( rc = pNode->hasChildren( pDb, &bHasChildren)))
        {
            goto Exit;
        }

        // If we had a value, then close off the line.
        if (uiDataType == XFLM_TEXT_TYPE ||
            uiDataType == XFLM_NUMBER_TYPE)
        {
            sprintf( (char *)pszLine, "</%s>", szName);
        }
        else
        {
            sprintf( (char *)pszLine, "%s>", bHasChildren ? "" : "/");
        }

        // Children
        if (bHasChildren && !bHadValue)
        {
            // Get the child node.
            if (RC_BAD( rc = pNode->getFirstChild( pDb, &pNode)))
            {
                goto Exit;
            }
            uiNextLevel++;
            continue;
        }

        // Siblings - first one may be an attribute.

        if (RC_BAD( rc = pNode->hasNextSibling( pDb, &bHasNextSibling)))
        {
            goto Exit;
        }

        if (bHasNextSibling)
        {

            if (RC_BAD( rc = pNode->getNextSibling( pDb, &pNode)))
            {
                if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
                {
                    rc = NE_XFLM_OK;
                    goto GetParent;
                }
                goto Exit;
            }
            continue;
        }

GetParent:

        bUnwinding = FALSE;

        if (uiNextLevel)
        {
            --uiNextLevel;
        }

        if (RC_BAD( rc = pNode->getParentNode( pDb, &pNode)))
        {
            if (rc != NE_XFLM_DOM_NODE_NOT_FOUND)
            {
                goto Exit;
            }

            rc = NE_XFLM_OK;

            pNode->Release();
            pNode = NULL;

        }
        else
        {

            if (RC_BAD( rc = pNode->hasNextSibling( pDb, &bHasNextSibling)))
            {
                goto Exit;
            }

            if (bHasNextSibling)
            {
                if (RC_BAD( rc = pNode->getNextSibling( pDb, &pNode)))
                {
                    goto Exit;
                }
                continue;
            }

            bUnwinding = TRUE;
        }
    }
 

// Print the last line.
printMessage( (char *)ucLine);

Exit:
 

    return( rc);
 

}


/*=============================================================================
Desc:    Formatter for displaying attributes.
=============================================================================*/

RCODE processAttributes(

    IF_Db *                     pDb,
    IF_DOMNode *        pNode,
    FLMBYTE **            ppszLine
    )
{
    RCODE                 rc = NE_XFLM_OK;
    IF_DOMNode *    pAttrNode = NULL;
    FLMBOOL            bHasAttrs;
    FLMBOOL            bHasNextSibling;
    char                        szName[ 50];
    FLMUINT             uiNameLen;
    FLMBYTE *          pszLine = *ppszLine;
    FLMUINT             uiDataType;
    FLMBYTE             szTemp[ 200];
    FLMUINT              uiTemp;

    if (RC_BAD( rc = pNode->hasAttributes( pDb, &bHasAttrs)))
    {
        goto Exit;
    }

    if (!bHasAttrs)
    {
        // rc == NE_XFLM_OK
        goto Exit;
    }

    // We have attributes. Let's get them and add them to the line.
    if (RC_BAD( rc = pNode->getFirstAttribute( pDb, &pAttrNode)))
    {
        goto Exit;
    }

    for (;;)
    {
        if (RC_BAD( rc = pAttrNode->getLocalName( pDb, szName, 50, &uiNameLen)))
        {
            goto Exit;
        }

        sprintf( (char *)pszLine, " %s", szName);
        pszLine += (uiNameLen + 1);

        if (RC_BAD( rc = pAttrNode->getDataType( pDb, &uiDataType)))
        {
            goto Exit;
        }

        switch (uiDataType)
        {
            case XFLM_TEXT_TYPE:
            {
                if (RC_BAD( rc = pAttrNode->getNative( pDb, (char *)szTemp, 0, 200, &uiTemp)))
                {
                    goto Exit;
                }
                sprintf( (char *)pszLine, "=\"%s\"", szTemp);
                pszLine += (uiTemp + 3);
                break;
            }
            case XFLM_NUMBER_TYPE:
            {
                if (RC_BAD( rc = pAttrNode->getUINT( pDb, &uiTemp)))
                {
                    goto Exit;
                }
                sprintf( (char *)szTemp, "%u", uiTemp);
                sprintf( (char *)pszLine, "=\"%s\"", szTemp);
                pszLine += (strlen( (char *)szTemp) + 3);
                break;
            }
            // Could also get binary data, but we won't handle it here.
        }

        // Get the next attribute
        if (RC_BAD( rc = pAttrNode->hasNextSibling( pDb, &bHasNextSibling)))
        {
            goto Exit;
        }

        if (!bHasNextSibling)
        {
            break;
        }

        // Get the next sibling.
        if (RC_BAD( rc = pAttrNode->getNextSibling( pDb, &pAttrNode)))
        {
            if (rc == NE_XFLM_DOM_NODE_NOT_FOUND)
            {
                rc = NE_XFLM_OK;
                break;
            }
            goto Exit;
        }
    }

    // Update the line pointer on the way out.

    *ppszLine = pszLine;

Exit:

    if (pAttrNode)
    {
        pAttrNode->Release();
    }

    return rc;
}