/*!---------------------------------------------------------------------
  @file           RTE_Installation.cpp
  @author         JoergM, RobinW
  @brief          DBM: SAPDB Instance and Database Registration and Management Interfaces
  @see            

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2001-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under 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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  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.
    ========== licence end



\endif
---------------------------------------------------------------------*/


#if defined(RWTEST)
#define ASSERT_STATE_OPTION SAPDBERR_OFF
#define ASSERT_ARGUMENT_OPTION SAPDBERR_OFF
#define ASSERT_RANGE_OPTION SAPDBERR_OFF
#endif

/*===========================================================================*
 *  INCLUDES                                                                 *
 *===========================================================================*/

#include "SAPDBCommon/SAPDB_Types.hpp"
#include "RunTime/RTE_Types.hpp"
#include "RunTime/RTE_MessageList.hpp"
#include "RunTime/RTE_Message.hpp"
#include "RunTime/RTE_Messages.hpp"
#include "RunTime/RTE_DBRegister.hpp"
#include "RunTime/RTE_Installation.hpp"
#include "RunTime/RTE_Database.hpp"
#include "SAPDBCommon/SAPDB_sprintf.h"
#include "SAPDBCommon/SAPDB_Names.h"
#ifndef _WIN32
#include "RunTime/MemoryManagement/RTEMem_RteAllocator.hpp" /* nocheck */
#include "RunTime/RTE_saveUNIXcalls.h"                      /* nocheck */
#include "SAPDB/RunTime/System/RTESys_CommandPipe.h"        /* nocheck */
#include "heo01.h"                                          /* nocheck */
#include "RunTime/RTE_IniFileHandling.h"                    /* nocheck */
#include "gen41.h"                                          /* nocheck */ // for en41Get*FromFile()
#include "RunTime/System/RTESys_IO.h"                       /* nocheck */
#else
#include "gos00.h"                                          /* nocheck */
#include "RunTime/RTE_NTService.hpp"                        /* nocheck */
#include "RunTime/RTE_GetDBRootOfDB.h"                      /* nocheck */
#endif

/*===========================================================================*
 *  DEFINES                                                                  *
 *===========================================================================*/

 /*===========================================================================*
 *  MACROS                                                                   *
 *===========================================================================*/



/*===========================================================================*
 *  LOCAL CLASSES, STRUCTURES, TYPES, UNIONS ...                             *
 *===========================================================================*/


/*===========================================================================*
 *  STATIC/INLINE FUNCTION PROTOTYPES                                        *
 *===========================================================================*/


/*===========================================================================*
 *  METHODS                                                                  *
 *===========================================================================*/



#ifdef _WIN32
//-----------------------------------------------------------

Container_List<RTE_Database>* RTE_Installation::GetDatabases(SAPDBErr_MessageList &errList)
{
    Container_List<RTE_Database> *liste = new Container_List<RTE_Database>(RTEMem_Allocator::Instance());

    SAPDB_Int     active;
    APIRET        rc     = NO_ERROR;
    SQL_DBNAMEC   szServerDB;
    SAPDB_Char   *pszDBRoot;

    SAPDB_Int     serverType;
    if(m_Version.release < 7)
    {
        serverType = SERVER_TYPE_ADABAS_SERVERDB;
    }
    else
    {
        serverType = SERVER_TYPE_SERVERDB;
    }
    
    sql97_update(NULL);
    rc = sql97_first_db_gw_state ( serverType, szServerDB, &active );
    RTE_Database  *currentDataBase = NULL;
    while(ERROR_NO_MORE_FILES != rc)
    {
        pszDBRoot = RTE_getDBRootFromRegistry ( NULL, SERVER_TYPE_SERVERDB, szServerDB );
        if( NULL != pszDBRoot && 0 == _stricmp(pszDBRoot,m_dbRoot))
        {
            SAPDB_Char *modeName = strrchr(szServerDB,'(');

            RTE_SpeedInfo speedInfo = speedNone;

            if(NULL != modeName)
            {
                modeName--;
                *(modeName) = 0;
                modeName += 2;
                SAPDB_Char *modeEnd = strchr(modeName , ')' );
                if(0 != modeEnd)
                {
                    *modeEnd = 0;
                }
            }
            else
            {
                modeName = "fast";
            }

            Container_List<RTE_SpeedInfo>::Iterator speedIterator; 
            speedIterator = m_speedList.Begin();
            
            while(speedIterator != 0)
            {
                if (strcmp (modeName, (char *)speedIterator->Name()) == 0) 
                {
                    speedInfo = *speedIterator;
                    break;
                }
                ++speedIterator;
            }

            if(!(speedInfo == speedNone))
            {
                if((NULL == currentDataBase) || (0 != strcmp(currentDataBase->GetDBName(),szServerDB)))
                {
                    if(NULL != currentDataBase)
                    {
                        liste->InsertFront(*currentDataBase);
                        delete currentDataBase;
                    }
                    currentDataBase = new RTE_Database(*this,pszDBRoot,szServerDB);
                    currentDataBase->m_speedList.Delete();  // we don't like the pre-filled speed list here as it may contain speeds that are not registered for this special database...
                }
                currentDataBase->m_speedList.InsertFront(speedInfo);
                if(active)
                {
                    currentDataBase->SetActiveSpeed(speedInfo);
                }
            }
        }
        rc = sql97_next_db_gw_state (szServerDB, &active);
    }
/* finally, don't forget the last database found... The previous ones have been inserted every time a new one was created*/
    if(NULL != currentDataBase)
    {
        liste->InsertFront(*currentDataBase);
        delete currentDataBase;
    }

    return liste;
}


#else   // UNIX

#define RTE_DBREGISTRATION_MAX_SPEED_NAME_LENGTH   24

static void readOneLine(RTE_FileHandle fd, void *buf, SAPDB_Long nbyte)
{
    SAPDB_Long bytesRead=0;

    while(bytesRead < nbyte-1)  /* the very last character is filled with the terminating zero-byte */
    {
        if(1 != RTE_save_read(fd,(SAPDB_Char *)buf+bytesRead,1))
        {
            break;
        }
        if('\n' == ((SAPDB_Char *)buf)[bytesRead])
        {
            break;
        }
        bytesRead++;
    }
    ((SAPDB_Char *)buf)[bytesRead] = 0;
}

RTE_FuserAccess::fuserState RTE_FuserAccess::GetAccess(void)
{
    if ( notChecked == m_fuserState )
    {
        CheckFuserExistence();
    }
    return m_fuserState;
}

void RTE_FuserAccess::CheckFuserExistence(void)
{
    // check if a fuser executable is accessible. It may be in the $PATH, it may be accessible
    // via /usr/sbin or not accessible at all. If it is accessible, calling it without arguments
    // produces something like "Usage:....." on stderr.
    m_fuserState = noAccess;
    FILE *p;
#define FUSER_BUFFERSIZE_   256
    SAPDB_Char buf[FUSER_BUFFERSIZE_];
    buf[0]=0;
    RTE_SystemRc rc;
    SAPDB_Int4 exitStatus;

    RTESys_CommandOutputPipeContext context;
    rc = RTESys_OpenCommandOutputPipe( context, "fuser" );
    if( RTE_SYSTEMRC_NO_ERROR == rc )
    {
        readOneLine(context.cmdOutput,buf,FUSER_BUFFERSIZE_);
        if(strstr(buf,"sage"))  // to match "usage" or "Usage"...
        {
            m_fuserState = inPath;
        }
        RTESys_CloseCommandOutputPipe( context, exitStatus);
    }
    if( m_fuserState != inPath )    // if it was not found in the $PATH, try to call it explicitly in /usr/sbin
    {
        buf[0]=0;
        RTE_SystemRc rc;
        SAPDB_Int4 exitStatus;

        RTESys_CommandOutputPipeContext context;
        rc = RTESys_OpenCommandOutputPipe( context, "/usr/sbin/fuser" );
        if( RTE_SYSTEMRC_NO_ERROR == rc )
        {
            readOneLine(context.cmdOutput,buf,FUSER_BUFFERSIZE_);
            if(strstr(buf,"sage"))
            {
                m_fuserState = inUsrSbin;
            }
            RTESys_CloseCommandOutputPipe( context, exitStatus);
        }
    }
    if( m_fuserState == noAccess )    // if it was not found in the $PATH, try to call it explicitly in /sbin
    {
        buf[0]=0;
        RTE_SystemRc rc;
        SAPDB_Int4 exitStatus;

        RTESys_CommandOutputPipeContext context;
        rc = RTESys_OpenCommandOutputPipe( context, "/sbin/fuser" );
        if( RTE_SYSTEMRC_NO_ERROR == rc )
        {
            readOneLine(context.cmdOutput,buf,FUSER_BUFFERSIZE_);
            if(strstr(buf,"sage"))
            {
                m_fuserState = inSbin;
            }
            RTESys_CloseCommandOutputPipe( context, exitStatus);
        }
    }
}

RTE_FuserAccess::fuserState RTE_FuserAccess::m_fuserState = notChecked;

/*----------------------------------------*/


SAPDB_Bool RTE_Installation::dbHasSpecificProcess (
    SAPDB_Char *dbname,
    const SAPDB_Char *kernelpgm)
{
    SAPDB_Char   source [30];

    SAPDB_sprintf (source,30, "cat /tmp/db.%d", getpid() );

    return en41FindDatabaseProcess(source, dbname, kernelpgm);
}

/*----------------------------------------*/
#define NUMBER_OF_ELEMENTS_IN_PIDLIST_ 1

class PidArray
{
public:
    PidArray()
        : m_pNext(NULL)
    {}
    SAPDB_UInt  m_elements[NUMBER_OF_ELEMENTS_IN_PIDLIST_];
    PidArray   *m_pNext;
};

class PidList
{
public:
    PidList()
        : m_nextElementToFill(0)
        , m_currentPidArray(&m_firstPidArray)
    {}

    ~PidList()
    {
        PidArray *pNext = m_firstPidArray.m_pNext;
        while(pNext)
        {
            PidArray *pNextNext = pNext->m_pNext;
            (RTEMem_RteAllocator::Instance()).Deallocate(pNext);
            pNext = pNextNext;
        }
    }
    
    void Insert(SAPDB_UInt newPid)
    {
        if( NUMBER_OF_ELEMENTS_IN_PIDLIST_ == m_nextElementToFill )
        {
            PidArray *pNewArray = new(RTEMem_RteAllocator::Instance())PidArray;
            m_currentPidArray->m_pNext = pNewArray;
            m_nextElementToFill = 0;
            m_currentPidArray = pNewArray;
        }
        m_currentPidArray->m_elements[m_nextElementToFill] = newPid;
        m_nextElementToFill++;
    }

    void Dump(void)
    {
        SAPDB_UInt i;
        PidArray *pNext = m_firstPidArray.m_pNext;
        PidArray *currentArray = &m_firstPidArray;
        do
        {
            for(i = 0;i < (pNext ? NUMBER_OF_ELEMENTS_IN_PIDLIST_ : m_nextElementToFill);i++)
            {
                printf("%u\n",currentArray->m_elements[i]);
            }
            if(pNext)
            {
                currentArray = pNext;
                pNext = pNext->m_pNext;
            }
            else
            {
                break;
            }
        }
        while(1);
    }

    SAPDB_Bool Contains(SAPDB_UInt pid)
    {
        SAPDB_UInt i;
        PidArray *pNext = m_firstPidArray.m_pNext;
        PidArray *currentArray = &m_firstPidArray;
        do
        {
            for(i = 0;i < (pNext ? NUMBER_OF_ELEMENTS_IN_PIDLIST_ : m_nextElementToFill);i++)
            {
                if( pid == currentArray->m_elements[i])
                {
                    return true;
                }
            }
            if(pNext)
            {
                currentArray = pNext;
                pNext = pNext->m_pNext;
            }
            else
            {
                break;
            }
        }
        while(1);
        return false;
    }
private:
    PidArray    m_firstPidArray;
    SAPDB_UInt  m_nextElementToFill;
    PidArray  *m_currentPidArray;
};

void RTE_Installation::FillPidList(const SAPDB_Char *pExecutableName,PidList &pidList)
{
    RTE_Path fuserCall;
#define FUSER_BUFFERSIZE_   256

#ifdef _IBMR2
#define OS_SPECIFIC_FUSER_OPTION_ "-c"
#else
#define OS_SPECIFIC_FUSER_OPTION_ ""
#endif
    SAPDB_Char buf[FUSER_BUFFERSIZE_];

    const SAPDB_Char *pFuserExecutable = NULL;
    if( RTE_FuserAccess::inPath == m_fuserState )
    {
        pFuserExecutable = "fuser";
    }
    else if( RTE_FuserAccess::inUsrSbin == m_fuserState )
    {
        pFuserExecutable = "/usr/sbin/fuser";
    }
    else if( RTE_FuserAccess::inSbin == m_fuserState )
    {
       pFuserExecutable = "/sbin/fuser";
    }
    if( pFuserExecutable )
    {
        SAPDB_sprintf(fuserCall,sizeof(RTE_Path),"%s %s %s/pgm/%s"
            ,pFuserExecutable
            ,OS_SPECIFIC_FUSER_OPTION_
            ,m_dbRoot
            ,pExecutableName 
            );
        RTE_SystemRc rc;
        SAPDB_Int4 exitStatus;

        RTESys_CommandOutputPipeContext context;
        rc = RTESys_OpenCommandOutputPipe( context, fuserCall );
        if( RTE_SYSTEMRC_NO_ERROR == rc )
        {
            SAPDB_UInt index;
            SAPDB_UInt currentPid=0;
            do
            {
                buf[0]=0;
                readOneLine(context.cmdOutput,buf,FUSER_BUFFERSIZE_);
                if(buf[0])
                {
                    index = 0;
#define WAITING_FOR_BLANK_  0
#define WAITING_FOR_PID_    1
#define PARSING_PID_        2
                    SAPDB_Int4 state = WAITING_FOR_BLANK_;
                    while(index < FUSER_BUFFERSIZE_ && '\0' != buf[index])
                    {
                        if(isspace(buf[index])) // skip until the first space is found. The pids come after the first space
                        {
                            if(state == PARSING_PID_)
                            {
                                pidList.Insert(currentPid);
                            }
                            state = WAITING_FOR_PID_;
                            currentPid = 0;
                        }
                        else if(isdigit(buf[index]))
                        {
                            if(WAITING_FOR_PID_ == state)
                            {
                                state = PARSING_PID_;
                            }
                            if(PARSING_PID_ == state)
                            {
                                currentPid *= 10;
                                currentPid += (buf[index]-'0');
                            }
                        }
                        else
                        {
                            if(state == PARSING_PID_)
                            {
                                pidList.Insert(currentPid);
                                currentPid = 0;
                            }
                            state = WAITING_FOR_BLANK_;
                        }
                        index++;
                    }
                }
            }
            while(buf[0]);
            if(0 != currentPid)
            {
                pidList.Insert(currentPid);
            }
            RTESys_CloseCommandOutputPipe( context, exitStatus);
        }
    }
}

SAPDB_Bool RTE_Installation::MatchInProcFS(RTE_DBName &dbname,RTE_SpeedInfo speed,tsp00_TaskId pid)
{
    RTE_Path entryInProcFS;
    SAPDB_sprintf(entryInProcFS,sizeof(RTE_Path),"/proc/%d/cmdline",pid);
    SAPDB_Char contentsOfProcFSEntry[sizeof(RTE_Path)+1+sizeof(RTE_DBName)+1];
    RTE_FileHandle hendl;
    tsp00_VfReturn_Param ReturnStatus;
    RTESys_IOOpen ( hendl,
                    entryInProcFS,
                    RTESys_IOReadOnly,
                    false,
                    0,
                    ReturnStatus );
    if( vf_ok == ReturnStatus )
    {
        tRTESys_IOPosition BytesRead;
        RTESys_IORead(  hendl,
                        contentsOfProcFSEntry,
                        sizeof(contentsOfProcFSEntry),
                        BytesRead,
                        ReturnStatus );
        RTESys_IOClose(hendl,ReturnStatus);
        RTE_Path expectedExecutablePath;
        SAPDB_sprintf(expectedExecutablePath,sizeof(RTE_Path),"%s/pgm/%s",m_dbRoot,speed.ExecutableName());
        SAPDB_Char *dbNameFromProcFS = contentsOfProcFSEntry+strlen(contentsOfProcFSEntry)+1;
        if( ( 0 == strcmp(expectedExecutablePath,contentsOfProcFSEntry)) 
         && ( 0 == strcmp(dbname,dbNameFromProcFS)))
        {
           return true;
        }
    }
    else    // if the open() of the entry in the /proc/ file system fails, fall back to the fuser method. 
    {
        if(!m_fuserHasBeenCalled)
        {
            // m_fuserState has been set to noAccess in the constructor, so its existence is checked here
            m_fuserState = RTE_FuserAccess::GetAccess();
            // Because of m_fuserState==noAccess, the pid list has not been filled in GetDatabases(), so this must be done here
            FillPidList(speedFast.ExecutableName(),*m_pPidList);
            FillPidList(speedQuick.ExecutableName(),*m_pPidList);
            FillPidList(speedSlow.ExecutableName(),*m_pPidList);
            FillPidList(speedTest.ExecutableName(),*m_pPidList);
            m_fuserHasBeenCalled = true;
        }
        return true;    // return true in order to force the check of the pid list in dbHasRunningProcess() !!
    }
    return false;
}

SAPDB_Bool RTE_Installation::dbHasRunningProcess ( RTE_DBName &dbname,RTE_SpeedInfo speed )
{
    SAPDB_UTF8 speedFromFile[RTE_DBREGISTRATION_MAX_SPEED_NAME_LENGTH];

    tsp00_TaskId pid;
    if ( 0 != en41GetPPIDFromFile(dbname, &pid) // does the ppid file exist?
      || pid == 0                               // does the ppid file contain a pid?
      || 0 != kill(pid,0)                       // is the process with that pid alive?
      || 0 != en41GetPIDFromFile(dbname, &pid)  // does the pid file exist?
      || pid == 0                               // does the pid file contain a pid?
      || 0 != kill(pid,0)                       // is the process with that pid alive?
#ifdef LINUX    // PTS 1137490: use /proc/ file system entries rather than fuser on linux, because fuser calls are expensive
      || !MatchInProcFS(dbname,speed,pid)
#endif
      || !((RTE_FuserAccess::noAccess == m_fuserState) || m_pPidList->Contains(pid))
      || 0 != en41GetDbSpeedFromFile(dbname, (char *)speedFromFile, RTE_DBREGISTRATION_MAX_SPEED_NAME_LENGTH)   // does the speed file exist?
      || 0 != strcasecmp((const char *)speedFromFile, (const char *)speed.Name() ) 
       )
    {                                           // any of conditions above not true --> not running
        return false;
    }
    else
    {
        return true;
    }
}

Container_List<RTE_Database>* RTE_Installation::GetDatabases(SAPDBErr_MessageList &errList)
{
    Container_List<RTE_Database> *liste = new Container_List<RTE_Database>(RTEMem_Allocator::Instance());

    RTE_RegistryHandle registry;
    RTE_IniFileErrtext errtext;
    RTE_IniFileResult rc;

    registry = RTE_OpenConfigEnum (SAPDB_DATABASES_INI_FILE, SAPDB_DATABASES_SECTION, errtext, rc);
    if (rc != SAPDB_INIFILE_RESULT_OK) 
    {
        return liste;
    }

    SAPDB_Bool buildProcessList;
    if ( m_Version.release > 7
      || ( m_Version.release == 7
        && m_Version.major >= 3 ))
        buildProcessList = false;
    else
        buildProcessList = true;

    if (buildProcessList) 
    {
        /* build process list ( needed only to support releases < 7.4 ) */
        char		cmdBuf [255];
        // TODO: try to avoid calling an external program!
        SAPDB_sprintf (cmdBuf, sizeof(cmdBuf),"ps_all > /tmp/db.%d 2>/dev/null", getpid ());
        system (cmdBuf);
    }

    RTE_RegistryLocation location;
    char real_mdbroot[PATH_MAX];
    if(0 != realpath(m_dbRoot,real_mdbroot))    // this means that m_dbRoot is not okay. Forget it.
    {
        RTE_Database  *currentDataBase = NULL;

        PidList pidsRunningKernel;
        m_pPidList = &pidsRunningKernel;

        FillPidList(speedFast.ExecutableName(),pidsRunningKernel);
        FillPidList(speedQuick.ExecutableName(),pidsRunningKernel);
        FillPidList(speedSlow.ExecutableName(),pidsRunningKernel);
        FillPidList(speedTest.ExecutableName(),pidsRunningKernel);

        int ok;
        do
        {
            RTE_DBName    dbname;
            RTE_Path      dbroot;
            RTE_IniFileErrtext errtext;
            RTE_IniFileResult rc;

            ok = RTE_NextConfigEnum (registry, dbname, sizeof(dbname), dbroot, sizeof(dbroot), location, errtext, rc);
            if (ok)
            {
                char real_dbroot[PATH_MAX];
                realpath(dbroot,real_dbroot);

                if ( 0 == strcmp(real_dbroot,real_mdbroot))
                {
                    currentDataBase = new RTE_Database(*this,dbroot,dbname);
                    currentDataBase->GetDBSpeeds().Initialize(m_speedList);

                    Container_List<RTE_SpeedInfo>::Iterator speedIterator; 
                    speedIterator = (currentDataBase->GetDBSpeeds()).Begin();

                    while(speedIterator != 0)
                    {
                        SAPDB_Bool running = false;                 
                        if(buildProcessList)
                        {
                            running =  dbHasSpecificProcess (dbname, (*speedIterator).ExecutableName()) ;
                        }
                        else
                        {
                            running = dbHasRunningProcess (dbname,*speedIterator);
                        }
                        if(running)
                        {
                            currentDataBase->SetActiveSpeed(*speedIterator);
                        }
                        ++speedIterator;
                    }
                    liste->InsertFront(*currentDataBase);
                }
            }
        }
        while(ok);
    }
    RTE_CloseConfigEnum (registry, errtext, rc);
	registry = 0;

    if (buildProcessList) 
    {
        RTE_Path	tmpName;
        SAPDB_sprintf ((char *)tmpName, sizeof(RTE_Path), "/tmp/db.%d", getpid () );
        unlink ((char *)tmpName);
    }
    return liste;
}

//-----------------------------------------------------------

#endif

struct DatabaseListAndIterator
{
    Container_List<RTE_Installation>                    *installationList;
    Container_List<RTE_Installation>::Iterator          *installationIterator;
    Container_List<RTE_Database>                        *databaseList;
    Container_List<RTE_Database>::Iterator              *databaseIterator;
    Container_List<RTE_SpeedInfo>                       *speedList;
    Container_List<RTE_SpeedInfo>::Iterator             *speedIterator;
    DatabaseListAndIterator()
    {
        installationList = NULL;
        installationIterator = NULL;
        databaseList = NULL;
        databaseIterator = NULL;
        speedList = NULL;
        speedIterator = NULL;
    };
};


#include "gsp09.h"

extern "C"
{
#include "heo05.h"
}
externC void sqlxopen_db_enum (
    void** handle,
    BOOLEAN fullinfo,
    tsp9_rte_xerror* xerror)
{
    SAPDBErr_MessageList err;

    DatabaseListAndIterator *listAndIterator = new DatabaseListAndIterator();

    listAndIterator->installationList = ((RTE_DBRegister::Instance()).GetInstallations());
    listAndIterator->installationIterator = new Container_List<RTE_Installation>::Iterator;
    *(listAndIterator->installationIterator) = listAndIterator->installationList->Begin();
    // if not even a single installation is found (a very theoretic case of cousrse), no iterarion can be done...
    if(!(*((listAndIterator)->installationIterator) == 0 ))
    {
        do
        {
            do
            {
                if(0 == listAndIterator->databaseIterator)
                {
                    listAndIterator->databaseList = ((*(listAndIterator->installationIterator))->GetDatabases(err));
                    listAndIterator->databaseIterator = new Container_List<RTE_Database>::Iterator;
                    *(listAndIterator->databaseIterator) = listAndIterator->databaseList->Begin();
                }
                else
                {
                    ++(*listAndIterator->databaseIterator);
                }
                if(*(listAndIterator->databaseIterator) == 0)
                {
                    while(*(listAndIterator->databaseIterator) == 0)
                    {
                        ++(*listAndIterator->installationIterator);
            
                        if(*((listAndIterator)->installationIterator) == 0 )
                            break;

                        listAndIterator->databaseList = ((*(listAndIterator->installationIterator))->GetDatabases(err));
                        listAndIterator->databaseIterator = new Container_List<RTE_Database>::Iterator;
                        *(listAndIterator->databaseIterator) = listAndIterator->databaseList->Begin();
                    }
                }
                if(*((listAndIterator)->installationIterator) == 0 )
                    break;

                Container_List<RTE_Database>::Iterator *iterator = listAndIterator->databaseIterator;
                RTE_Database *currentDatabase = &(*(*iterator));
                listAndIterator->speedList = &(currentDatabase->GetDBSpeeds());
                listAndIterator->speedIterator = new Container_List<RTE_SpeedInfo>::Iterator;
                *(listAndIterator->speedIterator) = listAndIterator->speedList->Begin();
            }
            while(*(listAndIterator->speedIterator) == 0);

            if(*((listAndIterator)->installationIterator) == 0 ) // hmm... we have searched through all installations withourt finding a single installed database with existing executables? Well, then leave.
                break;

            while(!(*(listAndIterator->speedIterator) == 0)
                && !(*(*(listAndIterator->speedIterator))).Exists())
            {
                ++(*(listAndIterator->speedIterator));
            }
            if(!(*(listAndIterator->speedIterator) == 0))
                break;
        }
        while(1);    
    }
    
    *handle = listAndIterator;

    xerror->xe_result = CtrlCmdOk_csp09;
}

externC void sqlxnext_db (
    void* handle,
    tsp9_rte_dbinfo* dbinfo,
    tsp9_rte_xerror* xerror)
{
    SAPDBErr_MessageList err;
    
    DatabaseListAndIterator *listAndIterator = (DatabaseListAndIterator *)handle;

    if(*((listAndIterator)->installationIterator) == 0 )
    {
        // if the current installation is not valid, quit 
        eo44eoshowError (xerror);
        return;
    }
    if(*((listAndIterator)->databaseIterator) == 0)
    {
        // if the current database is not valid, quit 
        eo44eoshowError (xerror);
        return;
    }

    while((*(listAndIterator->speedIterator) == 0) 
        || ! (*(listAndIterator->speedIterator))->Exists() )
    {
        ++(*listAndIterator->databaseIterator);

        while(*(listAndIterator->databaseIterator) == 0)
        {
            ++(*listAndIterator->installationIterator);
            if(*((listAndIterator)->installationIterator) == 0 )
            {
                // if there are no more valid installations, quit 
                eo44eoshowError (xerror);
                return;
            }
            delete listAndIterator->databaseList;
            listAndIterator->databaseList = ((*(listAndIterator->installationIterator))->GetDatabases(err));
            delete listAndIterator->databaseIterator;
            listAndIterator->databaseIterator = new Container_List<RTE_Database>::Iterator;
            *(listAndIterator->databaseIterator) = listAndIterator->databaseList->Begin();
        }
        listAndIterator->speedList = &((*(*(listAndIterator->databaseIterator))).GetDBSpeeds());
        delete listAndIterator->speedIterator;
        listAndIterator->speedIterator = new Container_List<RTE_SpeedInfo>::Iterator;
        *(listAndIterator->speedIterator) = listAndIterator->speedList->Begin();
        while(!(*((DatabaseListAndIterator *)handle)->speedIterator == 0)
            && (!(*(((DatabaseListAndIterator *)handle)->speedIterator))->Exists()))
        {
            ++(*((DatabaseListAndIterator *)handle)->speedIterator);
        }
    }
    
    RTE_Database *currentDatabase = &(*(*(listAndIterator->databaseIterator)));
    RTE_Installation currentInstallation = *(*((listAndIterator)->installationIterator));

    eo44initError (xerror);

    strcpy(dbinfo->dbname,currentDatabase->GetDBName());
    strcpy(dbinfo->dbroot,currentDatabase->GetDBRoot());
    dbinfo->version.no[0] = currentInstallation.GetVersion().release;
    dbinfo->version.no[1] = currentInstallation.GetVersion().major;
    dbinfo->version.no[2] = currentInstallation.GetVersion().minor;
    dbinfo->version.no[3] = currentInstallation.GetVersion().build;

    RTE_SpeedInfo currentSpeed = **(listAndIterator->speedIterator);

    if(speedFast == currentSpeed)
    {
        dbinfo->pgmKind         = csp9_fast_pgm;
    }
    else if(speedQuick == currentSpeed)
    {
        dbinfo->pgmKind         = csp9_quick_pgm;
    }
    else if(speedSlow == currentSpeed)
    {
        dbinfo->pgmKind         = csp9_slow_pgm;
    }
    else if(speedTest == currentSpeed)
    {
        dbinfo->pgmKind         = csp9_test_pgm;
    }

    if(currentDatabase->GetActiveSpeed() == currentSpeed)
    {
        dbinfo->state = csp9_state_running;
    }
    else
    {
        dbinfo->state = csp9_state_off;
    }

    // switch to the next speed
    do
    {
        ++(*((DatabaseListAndIterator *)handle)->speedIterator);
    }
    while(!(*((DatabaseListAndIterator *)handle)->speedIterator == 0)
        && (!(*(((DatabaseListAndIterator *)handle)->speedIterator))->Exists()));
}

externC void sqlxclose_db_enum (
    void* handle)
{
    delete ((DatabaseListAndIterator *)handle)->installationIterator;
    delete ((DatabaseListAndIterator *)handle)->installationList;
    delete ((DatabaseListAndIterator *)handle)->databaseIterator;
    delete ((DatabaseListAndIterator *)handle)->databaseList;
    delete ((DatabaseListAndIterator *)handle)->speedIterator;
    delete ((DatabaseListAndIterator *)handle);
}

/*===========================================================================*
 *  END OF CODE                                                              *
 *===========================================================================*/
