//===-- MIDriverMgr.cpp -----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// Third Party Headers:
#include "lldb/API/SBError.h"

// In-house headers:
#include "MIDriverMgr.h"
#include "MICmnResources.h"
#include "MICmnLog.h"
#include "MICmnLogMediumFile.h"
#include "MIDriver.h"
#include "MICmnStreamStdout.h"
#include "MIUtilSingletonHelper.h"

//++ ------------------------------------------------------------------------------------
// Details: CMIDriverMgr constructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMIDriverMgr::CMIDriverMgr(void)
    : m_pDriverCurrent(nullptr)
    , m_bInMi2Mode(false)
{
}

//++ ------------------------------------------------------------------------------------
// Details: CMIDriverMgr destructor.
// Type:    Overridden.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMIDriverMgr::~CMIDriverMgr(void)
{
    Shutdown();
}

//++ ------------------------------------------------------------------------------------
// Details: Initialize *this manager.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIDriverMgr::Initialize(void)
{
    m_clientUsageRefCnt++;

    ClrErrorDescription();

    if (m_bInitialized)
        return MIstatus::success;

    bool bOk = MIstatus::success;
    CMIUtilString errMsg;

    // Note initialisation order is important here as some resources depend on previous
    MI::ModuleInit<CMICmnLog>(IDS_MI_INIT_ERR_LOG, bOk, errMsg);
    MI::ModuleInit<CMICmnResources>(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg);

    m_bInitialized = bOk;

    if (!bOk)
    {
        CMIUtilString strInitError(CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_DRIVERMGR), errMsg.c_str()));
        SetErrorDescription(strInitError);
        return MIstatus::failure;
    }

    return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details: Unbind detach or release resources used by this server in general common
//          functionality shared between versions of any server interfaces implemented.
// Type:    Method.
// Args:    vbAppExitOk - (R) True = No problems, false = App exiting with problems (investigate!).
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIDriverMgr::Shutdown(void)
{
    // Do not want a ref counter because this function needs to be called how ever this
    // application stops running
    // if( --m_clientUsageRefCnt > 0 )
    //  return MIstatus::success;

    ClrErrorDescription();

    if (!m_bInitialized)
        return MIstatus::success;

    m_bInitialized = false;

    bool bOk = MIstatus::success;
    CMIUtilString errMsg;

    // Tidy up
    UnregisterDriverAll();

    // Note shutdown order is important here
    MI::ModuleShutdown<CMICmnResources>(IDE_MI_SHTDWN_ERR_RESOURCES, bOk, errMsg);
    MI::ModuleShutdown<CMICmnLog>(IDS_MI_SHTDWN_ERR_LOG, bOk, errMsg);

    if (!bOk)
    {
        SetErrorDescriptionn(MIRSRC(IDS_MI_SHTDWN_ERR_DRIVERMGR), errMsg.c_str());
    }

    return bOk;
}
//++ ------------------------------------------------------------------------------------
// Details: Unregister all the Driver registered with *this manager. The manager also
//          deletes
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIDriverMgr::UnregisterDriverAll(void)
{
    MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
    while (it != m_mapDriverIdToDriver.end())
    {
        IDriver *pDriver = (*it).second;
        pDriver->DoShutdown();

        // Next
        ++it;
    }

    m_mapDriverIdToDriver.clear();
    m_pDriverCurrent = NULL;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Register a driver with *this Driver Manager. Call SetUseThisDriverToDoWork()
//          inform the manager which driver is the one to the work. The manager calls
//          the driver's init function which must be successful in order to complete the
//          registration.
// Type:    Method.
// Args:    vrDriver    - (R) The driver to register.
//          vrDriverID  - (R) The driver's ID to lookup by.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIDriverMgr::RegisterDriver(const IDriver &vrDriver, const CMIUtilString &vrDriverID)
{
    if (HaveDriverAlready(vrDriver))
        return MIstatus::success;

    IDriver *pDriver = const_cast<IDriver *>(&vrDriver);
    if (!pDriver->SetId(vrDriverID))
        return MIstatus::failure;
    if (!pDriver->DoInitialize())
    {
        SetErrorDescriptionn(MIRSRC(IDS_DRIVERMGR_DRIVER_ERR_INIT), pDriver->GetName().c_str(), vrDriverID.c_str(),
                             pDriver->GetError().c_str());
        return MIstatus::failure;
    }

    MapPairDriverIdToDriver_t pr(vrDriverID, pDriver);
    m_mapDriverIdToDriver.insert(pr);

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Query the Driver Manager to see if *this manager has the driver already
//          registered.
// Type:    Method.
// Args:    vrDriver    - (R) The driver to query.
// Return:  True - registered.
//          False - not registered.
// Throws:  None.
//--
bool
CMIDriverMgr::HaveDriverAlready(const IDriver &vrDriver) const
{
    MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
    while (it != m_mapDriverIdToDriver.end())
    {
        const IDriver *pDrvr = (*it).second;
        if (pDrvr == &vrDriver)
            return true;

        // Next
        ++it;
    }

    return false;
}

//++ ------------------------------------------------------------------------------------
// Details: Unregister a driver from the Driver Manager. Call the SetUseThisDriverToDoWork()
//          function to define another driver to do work if the one being unregistered did
//          the work previously.
// Type:    Method.
// Args:    vrDriver    - (R) The driver to unregister.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIDriverMgr::UnregisterDriver(const IDriver &vrDriver)
{
    const IDriver *pDrvr = nullptr;
    MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
    while (it != m_mapDriverIdToDriver.end())
    {
        pDrvr = (*it).second;
        if (pDrvr == &vrDriver)
            break;

        // Next
        ++it;
    }
    m_mapDriverIdToDriver.erase(it);

    if (m_pDriverCurrent == pDrvr)
        m_pDriverCurrent = nullptr;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Specify the driver to do work. The Driver Manager drives this driver. Any
//          previous driver doing work is not called anymore (so be sure the previous
//          driver is in a tidy state before stopping it working).
// Type:    Method.
// Args:    vrADriver   - (R) A lldb::SBBroadcaster/IDriver derived object.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIDriverMgr::SetUseThisDriverToDoWork(const IDriver &vrADriver)
{
    m_pDriverCurrent = const_cast<IDriver *>(&vrADriver);

    const CMIUtilString msg(CMIUtilString::Format(MIRSRC(IDS_DRIVER_SAY_DRIVER_USING), m_pDriverCurrent->GetName().c_str()));
    m_pLog->Write(msg, CMICmnLog::eLogVerbosity_Log);

    m_bInMi2Mode = m_pDriverCurrent->GetDriverIsGDBMICompatibleDriver();

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Ask *this manager which driver is currently doing the work.
// Type:    Method.
// Args:    None.
// Return:  IDriver * - Pointer to a driver, NULL if there is no current working driver.
// Throws:  None.
//--
CMIDriverMgr::IDriver *
CMIDriverMgr::GetUseThisDriverToDoWork(void) const
{
    return m_pDriverCurrent;
}

//++ ------------------------------------------------------------------------------------
// Details: Call this function puts *this driver to work.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIDriverMgr::DriverMainLoop(void)
{
    if (m_pDriverCurrent != nullptr)
    {
        if (!m_pDriverCurrent->DoMainLoop())
        {
            const CMIUtilString errMsg(CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_MAINLOOP), m_pDriverCurrent->GetError().c_str()));
            CMICmnStreamStdout::Instance().Write(errMsg, true);
            return MIstatus::failure;
        }
    }
    else
    {
        const CMIUtilString errMsg(CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)));
        CMICmnStreamStdout::Instance().Write(errMsg, true);
        return MIstatus::failure;
    }

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Get the current driver to validate executable command line arguments.
// Type:    Method.
// Args:    argc        - (R)   An integer that contains the count of arguments that follow in
//                              argv. The argc parameter is always greater than or equal to 1.
//          argv        - (R)   An array of null-terminated strings representing command-line
//                              arguments entered by the user of the program. By convention,
//                              argv[0] is the command with which the program is invoked.
//          vpStdOut    - (R)   Point to a standard output stream.
//          vwbExiting  - (W)   True = *this want to exit, false = continue to work.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIDriverMgr::DriverParseArgs(const int argc, const char *argv[], FILE *vpStdOut, bool &vwbExiting)
{
    if (m_pDriverCurrent == nullptr)
    {
        const CMIUtilString errMsg(CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)));
        CMICmnStreamStdout::Instance().Write(errMsg, true);
        return MIstatus::failure;
    }

    const lldb::SBError error(m_pDriverCurrent->DoParseArgs(argc, argv, vpStdOut, vwbExiting));
    bool bOk = !error.Fail();
    if (!bOk)
    {
        CMIUtilString errMsg;
        const MIchar *pErrorCstr = error.GetCString();
        if (pErrorCstr != nullptr)
            errMsg = CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_PARSE_ARGS), m_pDriverCurrent->GetName().c_str(), pErrorCstr);
        else
            errMsg = CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_PARSE_ARGS_UNKNOWN), m_pDriverCurrent->GetName().c_str());

        bOk = CMICmnStreamStdout::Instance().Write(errMsg, true);
    }

    return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details: Retrieve the current driver's last error condition.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString - Text description.
// Throws:  None.
//--
CMIUtilString
CMIDriverMgr::DriverGetError(void) const
{
    if (m_pDriverCurrent != nullptr)
        return m_pDriverCurrent->GetError();
    else
    {
        const CMIUtilString errMsg(CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)));
        CMICmnStreamStdout::Instance().Write(errMsg, true);
    }

    return CMIUtilString();
}

//++ ------------------------------------------------------------------------------------
// Details: Retrieve the current driver's name.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString - Driver name.
//                          Empty string = no current working driver specified.
// Throws:  None.
//--
CMIUtilString
CMIDriverMgr::DriverGetName(void) const
{
    if (m_pDriverCurrent != nullptr)
        return m_pDriverCurrent->GetName();
    else
    {
        const CMIUtilString errMsg(CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)));
        CMICmnStreamStdout::Instance().Write(errMsg, true);
    }

    return CMIUtilString();
}

//++ ------------------------------------------------------------------------------------
// Details: Retrieve the current driver's debugger object.
// Type:    Method.
// Args:    None.
// Return:  lldb::SBDebugger * - Ptr to driver's debugger object.
//                             - NULL = no current working driver specified.
// Throws:  None.
//--
lldb::SBDebugger *
CMIDriverMgr::DriverGetTheDebugger(void)
{
    lldb::SBDebugger *pDebugger = nullptr;
    if (m_pDriverCurrent != nullptr)
        pDebugger = &m_pDriverCurrent->GetTheDebugger();
    else
    {
        const CMIUtilString errMsg(CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)));
        CMICmnStreamStdout::Instance().Write(errMsg, true);
    }

    return pDebugger;
}

//++ ------------------------------------------------------------------------------------
// Details: Check the arguments given on the command line. The main purpose of this
//          function is to check for the presence of the --interpreter option. Having
//          this option present tells *this manager to set the CMIDriver to do work. If
//          not use the LLDB driver. The following are options that are only handled by
//          the CMIDriverMgr are:
//              --help or -h
//              --interpreter
//              --version
//              --versionLong
//              --log
//              --executable
//              --log-dir
//          The above arguments are not handled by any driver object except for --executable.
//          The options --interpreter and --executable in code act very similar. The
//          --executable is necessary to differentiate whither the MI Driver is being using
//          by a client i.e. Eclipse or from the command line. Eclipse issues the option
//          --interpreter and also passes additional arguments which can be interpreted as an
//          executable if called from the command line. Using --executable tells the MI
//          Driver is being called the command line and that the executable argument is indeed
//          a specified executable an so actions commands to set up the executable for a
//          debug session. Using --interpreter on the commnd line does not action additional
//          commands to initialise a debug session and so be able to launch the process. The directory
//          where the log file is created is specified using --log-dir.
// Type:    Method.
// Args:    argc        - (R)   An integer that contains the count of arguments that follow in
//                              argv. The argc parameter is always greater than or equal to 1.
//          argv        - (R)   An array of null-terminated strings representing command-line
//                              arguments entered by the user of the program. By convention,
//                              argv[0] is the command with which the program is invoked.
//          vwbExiting  - (W)   True = *this want to exit, Reasons: help, invalid arg(s),
//                              version information only.
//                              False = Continue to work, start debugger i.e. Command
//                              interpreter.
// Return:  lldb::SBError - LLDB current error status.
// Throws:  None.
//--
bool
CMIDriverMgr::ParseArgs(const int argc, const char *argv[], bool &vwbExiting)
{
    bool bOk = MIstatus::success;

    vwbExiting = false;

    // Print MI application path to the Log file
    const CMIUtilString appPath(CMIUtilString::Format(MIRSRC(IDS_MI_APP_FILEPATHNAME), argv[0]));
    bOk = m_pLog->Write(appPath, CMICmnLog::eLogVerbosity_Log);

    // Print application arguments to the Log file
    const bool bHaveArgs(argc >= 2);
    CMIUtilString strArgs(MIRSRC(IDS_MI_APP_ARGS));
    if (!bHaveArgs)
    {
        strArgs += MIRSRC(IDS_WORD_NONE);
        bOk = bOk && m_pLog->Write(strArgs, CMICmnLog::eLogVerbosity_Log);
    }
    else
    {
        for (MIint i = 1; i < argc; i++)
        {
            strArgs += CMIUtilString::Format("%d:'%s' ", i, argv[i]);
        }
        bOk = bOk && m_pLog->Write(strArgs, CMICmnLog::eLogVerbosity_Log);
    }

    // Look for the command line options
    bool bHaveArgInterpret = false;
    bool bHaveArgVersion = false;
    bool bHaveArgVersionLong = false;
    bool bHaveArgLog = false;
    bool bHaveArgLogDir = false;
    bool bHaveArgHelp = false;
    CMIUtilString strLogDir;

    bHaveArgInterpret = true;
    if (bHaveArgs)
    {
        // CODETAG_MIDRIVE_CMD_LINE_ARG_HANDLING
        for (MIint i = 1; i < argc; i++)
        {
            // *** Add args to help in GetHelpOnCmdLineArgOptions() ***
            const CMIUtilString strArg(argv[i]);

            // Argument "--executable" is also check for in CMIDriver::ParseArgs()
            if ((0 == strArg.compare("--interpreter")) || // Given by the client such as Eclipse
                (0 == strArg.compare("--executable")))    // Used to specify that there is executable argument also on the command line
            {                                             // See fn description.
                bHaveArgInterpret = true;
            }
            if (0 == strArg.compare("--version"))
            {
                bHaveArgVersion = true;
            }
            if (0 == strArg.compare("--versionLong"))
            {
                bHaveArgVersionLong = true;
            }
            if (0 == strArg.compare("--log"))
            {
                bHaveArgLog = true;
            }
            if (0 == strArg.compare(0,10,"--log-dir="))
            {
                strLogDir = strArg.substr(10,CMIUtilString::npos);
                bHaveArgLogDir = true;
            }
            if ((0 == strArg.compare("--help")) || (0 == strArg.compare("-h")))
            {
                bHaveArgHelp = true;
            }
        }
    }

    if (bHaveArgLog)
    {
        CMICmnLog::Instance().SetEnabled(true);
    }

    if (bHaveArgLogDir)
    {
        bOk = bOk && CMICmnLogMediumFile::Instance().SetDirectory(strLogDir);
    }

    // Todo: Remove this output when MI is finished. It is temporary to persuade Ecllipse plugin to work.
    //       Eclipse reads this literally and will not work unless it gets this exact version text.
    // Handle --version option (ignore the --interpreter option if present)
    if (bHaveArgVersion)
    {
        vwbExiting = true;
        bOk = bOk && CMICmnStreamStdout::Instance().WriteMIResponse(MIRSRC(IDE_MI_VERSION_GDB));
        return bOk;
    }

    // Todo: Make this the --version when the the above --version version is removed
    // Handle --versionlong option (ignore the --interpreter option if present)
    if (bHaveArgVersionLong)
    {
        vwbExiting = true;
        bOk = bOk && CMICmnStreamStdout::Instance().WriteMIResponse(GetAppVersion());
        return bOk;
    }

    // Both '--help' and '--intepreter' means give help for MI only. Without
    // '--interpreter' help the LLDB driver is working and so help is for that.
    if (bHaveArgHelp && bHaveArgInterpret)
    {
        vwbExiting = true;
        bOk = bOk && CMICmnStreamStdout::Instance().WriteMIResponse(GetHelpOnCmdLineArgOptions());
        return bOk;
    }

    // This makes the assumption that there is at least one MI compatible
    // driver registered and one LLDB driver registerd and the CMIDriver
    // is the first one found.
    // ToDo: Implement a better solution that handle any order, any number
    // of drivers. Or this 'feature' may be removed if deemed not required.
    IDriver *pLldbDriver = GetFirstNonMIDriver();
    IDriver *pMi2Driver = GetFirstMIDriver();
    if (bHaveArgInterpret && (pMi2Driver != nullptr))
        bOk = bOk && SetUseThisDriverToDoWork(*pMi2Driver);
    else if (pLldbDriver != nullptr)
        bOk = bOk && SetUseThisDriverToDoWork(*pLldbDriver);
    else
    {
        if (bOk)
        {
            vwbExiting = true;
            const CMIUtilString msg(MIRSRC(IDS_DRIVER_ERR_NON_REGISTERED));
            bOk = bOk && CMICmnStreamStdout::Instance().WriteMIResponse(msg);
        }
    }

    return bOk;
}

//++ ------------------------------------------------------------------------------------
// Details: Return formatted application version and name information.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString - Text data.
// Throws:  None.
//--
CMIUtilString
CMIDriverMgr::GetAppVersion(void) const
{
    const CMIUtilString strProj(MIRSRC(IDS_PROJNAME));
    const CMIUtilString strVsn(CMIDriver::Instance().GetVersionDescription());
    const CMIUtilString strGdb(MIRSRC(IDE_MI_VERSION_GDB));
    const CMIUtilString strVrsnInfo(CMIUtilString::Format("%s\n%s\n%s", strProj.c_str(), strVsn.c_str(), strGdb.c_str()));

    return strVrsnInfo;
}

//++ ------------------------------------------------------------------------------------
// Details: Return formatted help information on all the MI command line options.
// Type:    Method.
// Args:    None.
// Return:  CMIUtilString - Text data.
// Throws:  None.
//--
CMIUtilString
CMIDriverMgr::GetHelpOnCmdLineArgOptions(void) const
{
    const CMIUtilString pHelp[] = {
        MIRSRC(IDE_MI_APP_DESCRIPTION),
        MIRSRC(IDE_MI_APP_INFORMATION),
        MIRSRC(IDE_MI_APP_ARG_USAGE),
        MIRSRC(IDE_MI_APP_ARG_HELP),
        MIRSRC(IDE_MI_APP_ARG_VERSION),
        MIRSRC(IDE_MI_APP_ARG_VERSION_LONG),
        MIRSRC(IDE_MI_APP_ARG_INTERPRETER),
        MIRSRC(IDE_MI_APP_ARG_EXECUTEABLE),
        CMIUtilString::Format(MIRSRC(IDE_MI_APP_ARG_APP_LOG), CMICmnLogMediumFile::Instance().GetFileName().c_str()),
        MIRSRC(IDE_MI_APP_ARG_APP_LOG_DIR),
        MIRSRC(IDE_MI_APP_ARG_EXECUTABLE),
        MIRSRC(IDS_CMD_QUIT_HELP),
        MIRSRC(IDE_MI_APP_ARG_EXAMPLE)};
    const MIuint nHelpItems = sizeof pHelp / sizeof pHelp[0];
    CMIUtilString strHelp;
    for (MIuint i = 0; i < nHelpItems; i++)
    {
        strHelp += pHelp[i];
        strHelp += "\n\n";
    }

    return strHelp;
}

//++ ------------------------------------------------------------------------------------
// Details: Search the registered drivers and return the first driver which says it is
//          GDB/MI compatible i.e. the CMIDriver class.
// Type:    Method.
// Args:    None.
// Return:  IDriver * - Ptr to driver, NULL = no driver found.
// Throws:  None.
//--
CMIDriverMgr::IDriver *
CMIDriverMgr::GetFirstMIDriver(void) const
{
    IDriver *pDriver = nullptr;
    MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
    while (it != m_mapDriverIdToDriver.end())
    {
        const CMIUtilString &rDrvId = (*it).first;
        MIunused(rDrvId);
        IDriver *pDvr = (*it).second;
        if (pDvr->GetDriverIsGDBMICompatibleDriver())
        {
            pDriver = pDvr;
            break;
        }

        // Next
        ++it;
    }

    return pDriver;
}

//++ ------------------------------------------------------------------------------------
// Details: Search the registered drivers and return the first driver which says it is
//          not GDB/MI compatible i.e. the LLDB Driver class.
// Type:    Method.
// Args:    None.
// Return:  IDriver * - Ptr to driver, NULL = no driver found.
// Throws:  None.
//--
CMIDriverMgr::IDriver *
CMIDriverMgr::GetFirstNonMIDriver(void) const
{
    IDriver *pDriver = nullptr;
    MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin();
    while (it != m_mapDriverIdToDriver.end())
    {
        const CMIUtilString &rDrvId = (*it).first;
        MIunused(rDrvId);
        IDriver *pDvr = (*it).second;
        if (!pDvr->GetDriverIsGDBMICompatibleDriver())
        {
            pDriver = pDvr;
            break;
        }

        // Next
        ++it;
    }

    return pDriver;
}

//++ ------------------------------------------------------------------------------------
// Details: Search the registered drivers and return driver with the specified ID.
// Type:    Method.
// Args:    vrDriverId  - (R) ID of a driver.
// Return:  IDriver * - Ptr to driver, NULL = no driver found.
// Throws:  None.
//--
CMIDriverMgr::IDriver *
CMIDriverMgr::GetDriver(const CMIUtilString &vrDriverId) const
{
    MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.find(vrDriverId);
    if (it == m_mapDriverIdToDriver.end())
        return nullptr;

    IDriver *pDriver = (*it).second;

    return pDriver;
}


//++ ------------------------------------------------------------------------------------
// Details: Gets called when lldb-mi gets a signal. Passed signal to current driver.
//
// Type:    Method.
// Args:    signal that was delivered
// Return:  None.
// Throws:  None.
//--
void
CMIDriverMgr::DeliverSignal(int signal)
{
    if (m_pDriverCurrent != nullptr)
        m_pDriverCurrent->DeliverSignal(signal);
}
