text: //===-- MIDriver.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 <fstream>
  #include "lldb/API/SBError.h"
  
  // In-house headers:
  #include "MIDriver.h"
  #include "MICmnResources.h"
  #include "MICmnLog.h"
  #include "MICmdMgr.h"
  #include "MICmnLLDBDebugger.h"
  #include "MICmnMIResultRecord.h"
  #include "MICmnMIValueConst.h"
  #include "MICmnThreadMgrStd.h"
  #include "MIUtilDebug.h"
  #include "MIUtilSingletonHelper.h"
  #include "MICmnStreamStdout.h"
  #include "MICmnStreamStderr.h"
  #include "MICmdArgValFile.h"
  #include "MICmdArgValString.h"
  #include "MICmnConfig.h"
  #include "MICmnLLDBDebugSessionInfo.h"
  
  // Instantiations:
  #if _DEBUG
  const CMIUtilString CMIDriver::ms_constMIVersion = MIRSRC(IDS_MI_VERSION_DESCRIPTION_DEBUG);
  #else
  const CMIUtilString CMIDriver::ms_constMIVersion = MIRSRC(IDS_MI_VERSION_DESCRIPTION); // Matches version in resources file
  #endif // _DEBUG
  const CMIUtilString CMIDriver::ms_constAppNameShort(MIRSRC(IDS_MI_APPNAME_SHORT));
  const CMIUtilString CMIDriver::ms_constAppNameLong(MIRSRC(IDS_MI_APPNAME_LONG));
  
  //++ ------------------------------------------------------------------------------------
  // Details: CMIDriver constructor.
  // Type:    Method.
  // Args:    None.
  // Return:  None.
  // Throws:  None.
  //--
  CMIDriver::CMIDriver(void)
      : m_bFallThruToOtherDriverEnabled(false)
      , m_bDriverIsExiting(false)
      , m_handleMainThread(0)
      , m_rStdin(CMICmnStreamStdin::Instance())
      , m_rLldbDebugger(CMICmnLLDBDebugger::Instance())
      , m_rStdOut(CMICmnStreamStdout::Instance())
      , m_eCurrentDriverState(eDriverState_NotRunning)
      , m_bHaveExecutableFileNamePathOnCmdLine(false)
      , m_bDriverDebuggingArgExecutable(false)
      , m_bHaveCommandFileNamePathOnCmdLine(false)
  {
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: CMIDriver destructor.
  // Type:    Overridden.
  // Args:    None.
  // Return:  None.
  // Throws:  None.
  //--
  CMIDriver::~CMIDriver(void)
  {
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Set whether *this driver (the parent) is enabled to pass a command to its
  //          fall through (child) driver to interpret the command and do work instead
  //          (if *this driver decides it can't handle the command).
  // Type:    Method.
  // Args:    vbYes   - (R) True = yes fall through, false = do not pass on command.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::SetEnableFallThru(const bool vbYes)
  {
      m_bFallThruToOtherDriverEnabled = vbYes;
      return MIstatus::success;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Get whether *this driver (the parent) is enabled to pass a command to its
  //          fall through (child) driver to interpret the command and do work instead
  //          (if *this driver decides it can't handle the command).
  // Type:    Method.
  // Args:    None.
  // Return:  bool - True = yes fall through, false = do not pass on command.
  // Throws:  None.
  //--
  bool
  CMIDriver::GetEnableFallThru(void) const
  {
      return m_bFallThruToOtherDriverEnabled;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Retrieve MI's application name of itself.
  // Type:    Method.
  // Args:    None.
  // Return:  CMIUtilString & - Text description.
  // Throws:  None.
  //--
  const CMIUtilString &
  CMIDriver::GetAppNameShort(void) const
  {
      return ms_constAppNameShort;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Retrieve MI's application name of itself.
  // Type:    Method.
  // Args:    None.
  // Return:  CMIUtilString & - Text description.
  // Throws:  None.
  //--
  const CMIUtilString &
  CMIDriver::GetAppNameLong(void) const
  {
      return ms_constAppNameLong;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Retrieve MI's version description of itself.
  // Type:    Method.
  // Args:    None.
  // Return:  CMIUtilString & - Text description.
  // Throws:  None.
  //--
  const CMIUtilString &
  CMIDriver::GetVersionDescription(void) const
  {
      return ms_constMIVersion;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Initialize setup *this driver ready for use.
  // Type:    Method.
  // Args:    None.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::Initialize(void)
  {
      m_eCurrentDriverState = eDriverState_Initialising;
      m_clientUsageRefCnt++;
  
      ClrErrorDescription();
  
      if (m_bInitialized)
          return MIstatus::success;
  
      bool bOk = MIstatus::success;
      CMIUtilString errMsg;
  
      // Initialize all of the modules we depend on
      MI::ModuleInit<CMICmnLog>(IDS_MI_INIT_ERR_LOG, bOk, errMsg);
      MI::ModuleInit<CMICmnStreamStdout>(IDS_MI_INIT_ERR_STREAMSTDOUT, bOk, errMsg);
      MI::ModuleInit<CMICmnStreamStderr>(IDS_MI_INIT_ERR_STREAMSTDERR, bOk, errMsg);
      MI::ModuleInit<CMICmnResources>(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg);
      MI::ModuleInit<CMICmnThreadMgrStd>(IDS_MI_INIT_ERR_THREADMANAGER, bOk, errMsg);
      MI::ModuleInit<CMICmnStreamStdin>(IDS_MI_INIT_ERR_STREAMSTDIN, bOk, errMsg);
      MI::ModuleInit<CMICmdMgr>(IDS_MI_INIT_ERR_CMDMGR, bOk, errMsg);
      bOk &= m_rLldbDebugger.SetDriver(*this);
      MI::ModuleInit<CMICmnLLDBDebugger>(IDS_MI_INIT_ERR_LLDBDEBUGGER, bOk, errMsg);
  
      m_bExitApp = false;
  
      m_bInitialized = bOk;
  
      if (!bOk)
      {
          const CMIUtilString msg = CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_DRIVER), errMsg.c_str());
          SetErrorDescription(msg);
          return MIstatus::failure;
      }
  
      m_eCurrentDriverState = eDriverState_RunningNotDebugging;
  
      return bOk;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Unbind detach or release resources used by *this driver.
  // Type:    Method.
  // Args:    None.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::Shutdown(void)
  {
      if (--m_clientUsageRefCnt > 0)
          return MIstatus::success;
  
      if (!m_bInitialized)
          return MIstatus::success;
  
      m_eCurrentDriverState = eDriverState_ShuttingDown;
  
      ClrErrorDescription();
  
      bool bOk = MIstatus::success;
      CMIUtilString errMsg;
  
      // Shutdown all of the modules we depend on
      MI::ModuleShutdown<CMICmnLLDBDebugger>(IDS_MI_INIT_ERR_LLDBDEBUGGER, bOk, errMsg);
      MI::ModuleShutdown<CMICmdMgr>(IDS_MI_INIT_ERR_CMDMGR, bOk, errMsg);
      MI::ModuleShutdown<CMICmnStreamStdin>(IDS_MI_INIT_ERR_STREAMSTDIN, bOk, errMsg);
      MI::ModuleShutdown<CMICmnThreadMgrStd>(IDS_MI_INIT_ERR_THREADMANAGER, bOk, errMsg);
      MI::ModuleShutdown<CMICmnResources>(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg);
      MI::ModuleShutdown<CMICmnStreamStderr>(IDS_MI_INIT_ERR_STREAMSTDERR, bOk, errMsg);
      MI::ModuleShutdown<CMICmnStreamStdout>(IDS_MI_INIT_ERR_STREAMSTDOUT, bOk, errMsg);
      MI::ModuleShutdown<CMICmnLog>(IDS_MI_INIT_ERR_LOG, bOk, errMsg);
  
      if (!bOk)
      {
          SetErrorDescriptionn(MIRSRC(IDS_MI_SHUTDOWN_ERR), errMsg.c_str());
      }
  
      m_eCurrentDriverState = eDriverState_NotRunning;
  
      return bOk;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Work function. Client (the driver's user) is able to append their own message
  //          in to the MI's Log trace file.
  // Type:    Method.
  // Args:    vMessage          - (R) Client's text message.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::WriteMessageToLog(const CMIUtilString &vMessage)
  {
      CMIUtilString msg;
      msg = CMIUtilString::Format(MIRSRC(IDS_MI_CLIENT_MSG), vMessage.c_str());
      return m_pLog->Write(msg, CMICmnLog::eLogVerbosity_ClientMsg);
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: CDriverMgr calls *this driver initialize setup ready for use.
  // Type:    Overridden.
  // Args:    None.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::DoInitialize(void)
  {
      return CMIDriver::Instance().Initialize();
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: CDriverMgr calls *this driver to unbind detach or release resources used by
  //          *this driver.
  // Type:    Overridden.
  // Args:    None.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::DoShutdown(void)
  {
      return CMIDriver::Instance().Shutdown();
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Retrieve the name for *this driver.
  // Type:    Overridden.
  // Args:    None.
  // Return:  CMIUtilString & - Driver name.
  // Throws:  None.
  //--
  const CMIUtilString &
  CMIDriver::GetName(void) const
  {
      const CMIUtilString &rName = GetAppNameLong();
      const CMIUtilString &rVsn = GetVersionDescription();
      static CMIUtilString strName = CMIUtilString::Format("%s %s", rName.c_str(), rVsn.c_str());
  
      return strName;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Retrieve *this driver's last error condition.
  // Type:    Overridden.
  // Args:    None.
  // Return:  CMIUtilString - Text description.
  // Throws:  None.
  //--
  CMIUtilString
  CMIDriver::GetError(void) const
  {
      return GetErrorDescription();
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Call *this driver to return it's debugger.
  // Type:    Overridden.
  // Args:    None.
  // Return:  lldb::SBDebugger & - LLDB debugger object reference.
  // Throws:  None.
  //--
  lldb::SBDebugger &
  CMIDriver::GetTheDebugger(void)
  {
      return m_rLldbDebugger.GetTheDebugger();
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Specify another driver *this driver can call should this driver not be able
  //          to handle the client data input. DoFallThruToAnotherDriver() makes the call.
  // Type:    Overridden.
  // Args:    vrOtherDriver     - (R) Reference to another driver object.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::SetDriverToFallThruTo(const CMIDriverBase &vrOtherDriver)
  {
      m_pDriverFallThru = const_cast<CMIDriverBase *>(&vrOtherDriver);
  
      return m_pDriverFallThru->SetDriverParent(*this);
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Proxy function CMIDriverMgr IDriver interface implementation. *this driver's
  //          implementation called from here to match the existing function name of the
  //          original LLDB driver class (the extra indirection is not necessarily required).
  //          Check the arguments that were passed to this program to make sure they are
  //          valid and to get their argument values (if any).
  // Type:    Overridden.
  // 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)   Pointer to a standard output stream.
  //          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.
  //--
  lldb::SBError
  CMIDriver::DoParseArgs(const int argc, const char *argv[], FILE *vpStdOut, bool &vwbExiting)
  {
      return ParseArgs(argc, argv, vpStdOut, vwbExiting);
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Check the arguments that were passed to this program to make sure they are
  //          valid and to get their argument values (if any). The following are options
  //          that are only handled by *this driver:
  //              --executable <file>
  //              --source <file> or -s <file>
  //          The application's options --interpreter and --executable in code act very similar.
  //          The --executable is necessary to differentiate whether the MI Driver is being
  //          used by a client (e.g. 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
  //          it is being called from the command line and to prepare to launch the executable
  //          argument for a debug session. Using --interpreter on the command line does not
  //          issue additional commands to initialise a debug session.
  // Type:    Overridden.
  // 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)   Pointer to a standard output stream.
  //          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.
  //--
  lldb::SBError
  CMIDriver::ParseArgs(const int argc, const char *argv[], FILE *vpStdOut, bool &vwbExiting)
  {
      lldb::SBError errStatus;
      const bool bHaveArgs(argc >= 2);
  
      // *** Add any args handled here to GetHelpOnCmdLineArgOptions() ***
  
      // CODETAG_MIDRIVE_CMD_LINE_ARG_HANDLING
      // Look for the command line options
      bool bHaveExecutableFileNamePath = false;
      bool bHaveExecutableLongOption = false;
  
      if (bHaveArgs)
      {
          // Search right to left to look for filenames
          for (MIint i = argc - 1; i > 0; i--)
          {
              const CMIUtilString strArg(argv[i]);
              const CMICmdArgValFile argFile;
  
              // Check for a filename
              if (argFile.IsFilePath(strArg) || CMICmdArgValString(true, false, true).IsStringArg(strArg))
              {
                  // Is this the command file for the '-s' or '--source' options?
                  const CMIUtilString strPrevArg(argv[i - 1]);
                  if (strPrevArg.compare("-s") == 0 || strPrevArg.compare("--source") == 0)
                  {
                      m_strCmdLineArgCommandFileNamePath = strArg;
                      m_bHaveCommandFileNamePathOnCmdLine = true;
                      i--; // skip '-s' on the next loop
                      continue;
                  }
                  // Else, must be the executable
                  bHaveExecutableFileNamePath = true;
                  m_strCmdLineArgExecuteableFileNamePath = strArg;
                  m_bHaveExecutableFileNamePathOnCmdLine = true;
              }
              // Report error if no command file was specified for the '-s' or '--source' options
              else if (strArg.compare("-s") == 0 || strArg.compare("--source") == 0)
              {
                  vwbExiting = true;
                  const CMIUtilString errMsg = CMIUtilString::Format(MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_MISSING_INF), strArg.c_str());
                  errStatus.SetErrorString(errMsg.c_str());
                  break;
              }
              // This argument is also checked for in CMIDriverMgr::ParseArgs()
              else if (strArg.compare("--executable") == 0) // Used to specify that there is executable argument also on the command line
              {                                             // See fn description.
                  bHaveExecutableLongOption = true;
              }
          }
      }
  
      if (bHaveExecutableFileNamePath && bHaveExecutableLongOption)
      {
          SetDriverDebuggingArgExecutable();
      }
  
      return errStatus;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: A client can ask if *this driver is GDB/MI compatible.
  // Type:    Overridden.
  // Args:    None.
  // Return:  True - GBD/MI compatible LLDB front end.
  //          False - Not GBD/MI compatible LLDB front end.
  // Throws:  None.
  //--
  bool
  CMIDriver::GetDriverIsGDBMICompatibleDriver(void) const
  {
      return true;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Start worker threads for the driver.
  // Type:    Method.
  // Args:    None.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::StartWorkerThreads(void)
  {
      bool bOk = MIstatus::success;
  
      // Grab the thread manager
      CMICmnThreadMgrStd &rThreadMgr = CMICmnThreadMgrStd::Instance();
  
      // Start the event polling thread
      if (bOk && !rThreadMgr.ThreadStart<CMICmnLLDBDebugger>(m_rLldbDebugger))
      {
          const CMIUtilString errMsg = CMIUtilString::Format(MIRSRC(IDS_THREADMGR_ERR_THREAD_FAIL_CREATE),
                                                             CMICmnThreadMgrStd::Instance().GetErrorDescription().c_str());
          SetErrorDescriptionn(errMsg);
          return MIstatus::failure;
      }
  
      return bOk;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Stop worker threads for the driver.
  // Type:    Method.
  // Args:    None.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::StopWorkerThreads(void)
  {
      CMICmnThreadMgrStd &rThreadMgr = CMICmnThreadMgrStd::Instance();
      return rThreadMgr.ThreadAllTerminate();
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Call this function puts *this driver to work.
  //          This function is used by the application's main thread.
  // Type:    Overridden.
  // Args:    None.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::DoMainLoop(void)
  {
      if (!InitClientIDEToMIDriver()) // Init Eclipse IDE
      {
          SetErrorDescriptionn(MIRSRC(IDS_MI_INIT_ERR_CLIENT_USING_DRIVER));
          return MIstatus::failure;
      }
  
      if (!StartWorkerThreads())
          return MIstatus::failure;
  
      bool bOk = MIstatus::success;
  
      if (HaveExecutableFileNamePathOnCmdLine())
      {
          if (!LocalDebugSessionStartupExecuteCommands())
          {
              SetErrorDescription(MIRSRC(IDS_MI_INIT_ERR_LOCAL_DEBUG_SESSION));
              bOk = MIstatus::failure;
          }
      }
  
      // App is not quitting currently
      m_bExitApp = false;
  
      // Handle source file
      if (m_bHaveCommandFileNamePathOnCmdLine)
      {
          const bool bAsyncMode = false;
          ExecuteCommandFile(bAsyncMode);
      }
  
      // While the app is active
      while (bOk && !m_bExitApp)
      {
          CMIUtilString errorText;
          const char *pCmd = m_rStdin.ReadLine (errorText);
          if (pCmd != nullptr)
          {
              CMIUtilString lineText(pCmd);
              if (!lineText.empty ())
              {
                  // Check that the handler thread is alive (otherwise we stuck here)
                  assert(CMICmnLLDBDebugger::Instance().ThreadIsActive());
  
                  {
                      // Lock Mutex before processing commands so that we don't disturb an event
                      // being processed
                      CMIUtilThreadLock lock(CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex());
                      bOk = InterpretCommand(lineText);
                  }
  
                  // Draw prompt if desired
                  bOk = bOk && CMICmnStreamStdout::WritePrompt();
  
                  // Wait while the handler thread handles incoming events
                  CMICmnLLDBDebugger::Instance().WaitForHandleEvent();
              }
          }
      }
  
      // Signal that the application is shutting down
      DoAppQuit();
  
      // Close and wait for the workers to stop
      StopWorkerThreads();
  
      // Ensure that a new line is sent as the last act of the dying driver
      m_rStdOut.WriteMIResponse("\n", false);
  
      return MIstatus::success;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Set things in motion, set state etc that brings *this driver (and the
  //          application) to a tidy shutdown.
  //          This function is used by the application's main thread.
  // Type:    Method.
  // Args:    None.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::DoAppQuit(void)
  {
      bool bYesQuit = true;
  
      // Shutdown stuff, ready app for exit
      {
          CMIUtilThreadLock lock(m_threadMutex);
          m_bDriverIsExiting = true;
      }
  
      return bYesQuit;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: *this driver passes text commands to a fall through driver is it does not
  //          understand them (the LLDB driver).
  //          This function is used by the application's main thread.
  // Type:    Method.
  // Args:    vTextLine           - (R) Text data representing a possible command.
  //          vwbCmdYesValid      - (W) True = Command valid, false = command not handled.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::InterpretCommandFallThruDriver(const CMIUtilString &vTextLine, bool &vwbCmdYesValid)
  {
      MIunused(vTextLine);
      MIunused(vwbCmdYesValid);
  
      // ToDo: Implement when less urgent work to be done or decide remove as not required
      // bool bOk = MIstatus::success;
      // bool bCmdNotUnderstood = true;
      // if( bCmdNotUnderstood && GetEnableFallThru() )
      //{
      //  CMIUtilString errMsg;
      //  bOk = DoFallThruToAnotherDriver( vStdInBuffer, errMsg );
      //  if( !bOk )
      //  {
      //      errMsg = errMsg.StripCREndOfLine();
      //      errMsg = errMsg.StripCRAll();
      //      const CMIDriverBase * pOtherDriver = GetDriverToFallThruTo();
      //      const char * pName = pOtherDriver->GetDriverName().c_str();
      //      const char * pId = pOtherDriver->GetDriverId().c_str();
      //      const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDS_DRIVER_ERR_FALLTHRU_DRIVER_ERR ), pName, pId, errMsg.c_str() )
      //);
      //      m_pLog->WriteMsg( msg );
      //  }
      //}
      //
      // vwbCmdYesValid = bOk;
      // CMIUtilString strNot;
      // if( vwbCmdYesValid)
      //  strNot = CMIUtilString::Format( "%s ", MIRSRC( IDS_WORD_NOT ) );
      // const CMIUtilString msg( CMIUtilString::Format( MIRSRC( IDS_FALLTHRU_DRIVER_CMD_RECEIVED ), vTextLine.c_str(), strNot.c_str() ) );
      // m_pLog->WriteLog( msg );
  
      return MIstatus::success;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Retrieve the name for *this driver.
  // Type:    Overridden.
  // Args:    None.
  // Return:  CMIUtilString & - Driver name.
  // Throws:  None.
  //--
  const CMIUtilString &
  CMIDriver::GetDriverName(void) const
  {
      return GetName();
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Get the unique ID for *this driver.
  // Type:    Overridden.
  // Args:    None.
  // Return:  CMIUtilString & - Text description.
  // Throws:  None.
  //--
  const CMIUtilString &
  CMIDriver::GetDriverId(void) const
  {
      return GetId();
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: This function allows *this driver to call on another driver to perform work
  //          should this driver not be able to handle the client data input.
  //          SetDriverToFallThruTo() specifies the fall through to driver.
  //          Check the error message if the function returns a failure.
  // Type:    Overridden.
  // Args:    vCmd        - (R) Command instruction to interpret.
  //          vwErrMsg    - (W) Error description on command failing.
  // Return:  MIstatus::success - Command succeeded.
  //          MIstatus::failure - Command failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::DoFallThruToAnotherDriver(const CMIUtilString &vCmd, CMIUtilString &vwErrMsg)
  {
      bool bOk = MIstatus::success;
  
      CMIDriverBase *pOtherDriver = GetDriverToFallThruTo();
      if (pOtherDriver == nullptr)
          return bOk;
  
      return pOtherDriver->DoFallThruToAnotherDriver(vCmd, vwErrMsg);
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: *this driver provides a file stream to other drivers on which *this driver
  //          write's out to and they read as expected input. *this driver is passing
  //          through commands to the (child) pass through assigned driver.
  // Type:    Overrdidden.
  // Args:    None.
  // Return:  FILE * - Pointer to stream.
  // Throws:  None.
  //--
  FILE *
  CMIDriver::GetStdin(void) const
  {
      // Note this fn is called on CMIDriverMgr register driver so stream has to be
      // available before *this driver has been initialized! Flaw?
  
      // This very likely to change later to a stream that the pass thru driver
      // will read and we write to give it 'input'
      return stdin;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: *this driver provides a file stream to other pass through assigned drivers
  //          so they know what to write to.
  // Type:    Overidden.
  // Args:    None.
  // Return:  FILE * - Pointer to stream.
  // Throws:  None.
  //--
  FILE *
  CMIDriver::GetStdout(void) const
  {
      // Note this fn is called on CMIDriverMgr register driver so stream has to be
      // available before *this driver has been initialized! Flaw?
  
      // Do not want to pass through driver to write to stdout
      return NULL;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: *this driver provides a error file stream to other pass through assigned drivers
  //          so they know what to write to.
  // Type:    Overidden.
  // Args:    None.
  // Return:  FILE * - Pointer to stream.
  // Throws:  None.
  //--
  FILE *
  CMIDriver::GetStderr(void) const
  {
      // Note this fn is called on CMIDriverMgr register driver so stream has to be
      // available before *this driver has been initialized! Flaw?
  
      // This very likely to change later to a stream that the pass thru driver
      // will write to and *this driver reads from to pass on the CMICmnLog object
      return stderr;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Set a unique ID for *this driver. It cannot be empty.
  // Type:    Overridden.
  // Args:    vId - (R) Text description.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::SetId(const CMIUtilString &vId)
  {
      if (vId.empty())
      {
          SetErrorDescriptionn(MIRSRC(IDS_DRIVER_ERR_ID_INVALID), GetName().c_str(), vId.c_str());
          return MIstatus::failure;
      }
  
      m_strDriverId = vId;
      return MIstatus::success;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Get the unique ID for *this driver.
  // Type:    Overridden.
  // Args:    None.
  // Return:  CMIUtilString & - Text description.
  // Throws:  None.
  //--
  const CMIUtilString &
  CMIDriver::GetId(void) const
  {
      return m_strDriverId;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Interpret the text data and match against current commands to see if there
  //          is a match. If a match then the command is issued and actioned on. The
  //          text data if not understood by *this driver is past on to the Fall Thru
  //          driver.
  //          This function is used by the application's main thread.
  // Type:    Method.
  // Args:    vTextLine   - (R) Text data representing a possible command.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::InterpretCommand(const CMIUtilString &vTextLine)
  {
      const bool bNeedToRebroadcastStopEvent = m_rLldbDebugger.CheckIfNeedToRebroadcastStopEvent();
      bool bCmdYesValid = false;
      bool bOk = InterpretCommandThisDriver(vTextLine, bCmdYesValid);
      if (bOk && !bCmdYesValid)
          bOk = InterpretCommandFallThruDriver(vTextLine, bCmdYesValid);
  
      if (bNeedToRebroadcastStopEvent)
          m_rLldbDebugger.RebroadcastStopEvent();
  
      return bOk;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Helper function for CMIDriver::InterpretCommandThisDriver.
  //          Convert a CLI command to MI command (just wrap any CLI command
  //          into "<tokens>-interpreter-exec command \"<CLI command>\"").
  // Type:    Method.
  // Args:    vTextLine   - (R) Text data representing a possible command.
  // Return:  CMIUtilString   - The original MI command or converted CLI command.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  CMIUtilString
  CMIDriver::WrapCLICommandIntoMICommand(const CMIUtilString &vTextLine) const
  {
      // Tokens contain following digits
      static const CMIUtilString digits("0123456789");
  
      // Consider an algorithm on the following example:
      // 001-file-exec-and-symbols "/path/to/file"
      //
      // 1. Skip a command token
      // For example:
      // 001-file-exec-and-symbols "/path/to/file"
      // 001target create "/path/to/file"
      //    ^ -- command starts here (in both cases)
      // Also possible case when command not found:
      // 001
      //    ^ -- i.e. only tokens are present (or empty string at all)
      const size_t nCommandOffset = vTextLine.find_first_not_of(digits);
  
      // 2. Check if command is empty
      // For example:
      // 001-file-exec-and-symbols "/path/to/file"
      // 001target create "/path/to/file"
      //    ^ -- command not empty (in both cases)
      // or:
      // 001
      //    ^ -- command wasn't found
      const bool bIsEmptyCommand = (nCommandOffset == CMIUtilString::npos);
  
      // 3. Check and exit if it isn't a CLI command
      // For example:
      // 001-file-exec-and-symbols "/path/to/file"
      // 001
      //    ^ -- it isn't CLI command (in both cases)
      // or:
      // 001target create "/path/to/file"
      //    ^ -- it's CLI command
      const bool bIsCliCommand = !bIsEmptyCommand && (vTextLine.at(nCommandOffset) != '-');
      if (!bIsCliCommand)
          return vTextLine;
  
     // 4. Wrap CLI command to make it MI-compatible
     //
     // 001target create "/path/to/file"
     // ^^^ -- token
     const std::string vToken(vTextLine.begin(), vTextLine.begin() + nCommandOffset);
     // 001target create "/path/to/file"
     //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- CLI command
     const CMIUtilString vCliCommand(std::string(vTextLine, nCommandOffset).c_str());
  
     // 5. Escape special characters and embed the command in a string
     // Result: it looks like -- target create \"/path/to/file\".
     const std::string vShieldedCliCommand(vCliCommand.AddSlashes());
  
     // 6. Turn the CLI command into an MI command, as in:
     // 001-interpreter-exec command "target create \"/path/to/file\""
     // ^^^ -- token
     //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^                               ^ -- wrapper
     //                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- shielded CLI command
     return CMIUtilString::Format("%s-interpreter-exec command \"%s\"",
                                  vToken.c_str(), vShieldedCliCommand.c_str());
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Interpret the text data and match against current commands to see if there
  //          is a match. If a match then the command is issued and actioned on. If a
  //          command cannot be found to match then vwbCmdYesValid is set to false and
  //          nothing else is done here.
  //          This function is used by the application's main thread.
  // Type:    Method.
  // Args:    vTextLine           - (R) Text data representing a possible command.
  //          vwbCmdYesValid      - (W) True = Command valid, false = command not handled.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::InterpretCommandThisDriver(const CMIUtilString &vTextLine, bool &vwbCmdYesValid)
  {
      // Convert any CLI commands into MI commands
      const CMIUtilString vMITextLine(WrapCLICommandIntoMICommand(vTextLine));
  
      vwbCmdYesValid = false;
      bool bCmdNotInCmdFactor = false;
      SMICmdData cmdData;
      CMICmdMgr &rCmdMgr = CMICmdMgr::Instance();
      if (!rCmdMgr.CmdInterpret(vMITextLine, vwbCmdYesValid, bCmdNotInCmdFactor, cmdData))
          return MIstatus::failure;
  
      if (vwbCmdYesValid)
      {
          // For debugging only
          // m_pLog->WriteLog( cmdData.strMiCmdAll.c_str() );
  
          return ExecuteCommand(cmdData);
      }
  
      // Check for escape character, may be cursor control characters
      // This code is not necessary for application operation, just want to keep tabs on what
      // has been given to the driver to try and interpret.
      if (vMITextLine.at(0) == 27)
      {
          CMIUtilString logInput(MIRSRC(IDS_STDIN_INPUT_CTRL_CHARS));
          for (MIuint i = 0; i < vMITextLine.length(); i++)
          {
              logInput += CMIUtilString::Format("%d ", vMITextLine.at(i));
          }
          m_pLog->WriteLog(logInput);
          return MIstatus::success;
      }
  
      // Write to the Log that a 'command' was not valid.
      // Report back to the MI client via MI result record.
      CMIUtilString strNotInCmdFactory;
      if (bCmdNotInCmdFactor)
          strNotInCmdFactory = CMIUtilString::Format(MIRSRC(IDS_DRIVER_CMD_NOT_IN_FACTORY), cmdData.strMiCmd.c_str());
      const CMIUtilString strNot(CMIUtilString::Format("%s ", MIRSRC(IDS_WORD_NOT)));
      const CMIUtilString msg(
          CMIUtilString::Format(MIRSRC(IDS_DRIVER_CMD_RECEIVED), vMITextLine.c_str(), strNot.c_str(), strNotInCmdFactory.c_str()));
      const CMICmnMIValueConst vconst = CMICmnMIValueConst(msg);
      const CMICmnMIValueResult valueResult("msg", vconst);
      const CMICmnMIResultRecord miResultRecord(cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, valueResult);
      const bool bOk = m_rStdOut.WriteMIResponse(miResultRecord.GetString());
  
      // Proceed to wait for or execute next command
      return bOk;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Having previously had the potential command validated and found valid now
  //          get the command executed.
  //          This function is used by the application's main thread.
  // Type:    Method.
  // Args:    vCmdData    - (RW) Command meta data.
  // Return:  MIstatus::success - Functional succeeded.
  //          MIstatus::failure - Functional failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::ExecuteCommand(const SMICmdData &vCmdData)
  {
      CMICmdMgr &rCmdMgr = CMICmdMgr::Instance();
      return rCmdMgr.CmdExecute(vCmdData);
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Set the MI Driver's exit application flag. The application checks this flag
  //          after every stdin line is read so the exit may not be instantaneous.
  //          If vbForceExit is false the MI Driver queries its state and determines if is
  //          should exit or continue operating depending on that running state.
  //          This is related to the running state of the MI driver.
  // Type:    Overridden.
  // Args:    None.
  // Return:  None.
  // Throws:  None.
  //--
  void
  CMIDriver::SetExitApplicationFlag(const bool vbForceExit)
  {
      if (vbForceExit)
      {
          CMIUtilThreadLock lock(m_threadMutex);
          m_bExitApp = true;
          return;
      }
  
      // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM
      // Did we receive a SIGINT from the client during a running debug program, if
      // so then SIGINT is not to be taken as meaning kill the MI driver application
      // but halt the inferior program being debugged instead
      if (m_eCurrentDriverState == eDriverState_RunningDebugging)
      {
          InterpretCommand("-exec-interrupt");
          return;
      }
  
      m_bExitApp = true;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Get the  MI Driver's exit exit application flag.
  //          This is related to the running state of the MI driver.
  // Type:    Method.
  // Args:    None.
  // Return:  bool    - True = MI Driver is shutting down, false = MI driver is running.
  // Throws:  None.
  //--
  bool
  CMIDriver::GetExitApplicationFlag(void) const
  {
      return m_bExitApp;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Get the current running state of the MI Driver.
  // Type:    Method.
  // Args:    None.
  // Return:  DriverState_e   - The current running state of the application.
  // Throws:  None.
  //--
  CMIDriver::DriverState_e
  CMIDriver::GetCurrentDriverState(void) const
  {
      return m_eCurrentDriverState;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Set the current running state of the MI Driver to running and currently not in
  //          a debug session.
  // Type:    Method.
  // Return:  MIstatus::success - Functionality succeeded.
  //          MIstatus::failure - Functionality failed.
  // Return:  DriverState_e   - The current running state of the application.
  // Throws:  None.
  //--
  bool
  CMIDriver::SetDriverStateRunningNotDebugging(void)
  {
      // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM
  
      if (m_eCurrentDriverState == eDriverState_RunningNotDebugging)
          return MIstatus::success;
  
      // Driver cannot be in the following states to set eDriverState_RunningNotDebugging
      switch (m_eCurrentDriverState)
      {
          case eDriverState_NotRunning:
          case eDriverState_Initialising:
          case eDriverState_ShuttingDown:
          {
              SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR));
              return MIstatus::failure;
          }
          case eDriverState_RunningDebugging:
          case eDriverState_RunningNotDebugging:
              break;
          case eDriverState_count:
              SetErrorDescription(
                  CMIUtilString::Format(MIRSRC(IDS_CODE_ERR_INVALID_ENUMERATION_VALUE), "SetDriverStateRunningNotDebugging()"));
              return MIstatus::failure;
      }
  
      // Driver must be in this state to set eDriverState_RunningNotDebugging
      if (m_eCurrentDriverState != eDriverState_RunningDebugging)
      {
          SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR));
          return MIstatus::failure;
      }
  
      m_eCurrentDriverState = eDriverState_RunningNotDebugging;
  
      return MIstatus::success;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Set the current running state of the MI Driver to running and currently not in
  //          a debug session. The driver's state must in the state running and in a
  //          debug session to set this new state.
  // Type:    Method.
  // Return:  MIstatus::success - Functionality succeeded.
  //          MIstatus::failure - Functionality failed.
  // Return:  DriverState_e   - The current running state of the application.
  // Throws:  None.
  //--
  bool
  CMIDriver::SetDriverStateRunningDebugging(void)
  {
      // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM
  
      if (m_eCurrentDriverState == eDriverState_RunningDebugging)
          return MIstatus::success;
  
      // Driver cannot be in the following states to set eDriverState_RunningDebugging
      switch (m_eCurrentDriverState)
      {
          case eDriverState_NotRunning:
          case eDriverState_Initialising:
          case eDriverState_ShuttingDown:
          {
              SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR));
              return MIstatus::failure;
          }
          case eDriverState_RunningDebugging:
          case eDriverState_RunningNotDebugging:
              break;
          case eDriverState_count:
              SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_CODE_ERR_INVALID_ENUMERATION_VALUE), "SetDriverStateRunningDebugging()"));
              return MIstatus::failure;
      }
  
      // Driver must be in this state to set eDriverState_RunningDebugging
      if (m_eCurrentDriverState != eDriverState_RunningNotDebugging)
      {
          SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR));
          return MIstatus::failure;
      }
  
      m_eCurrentDriverState = eDriverState_RunningDebugging;
  
      return MIstatus::success;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Prepare the client IDE so it will start working/communicating with *this MI
  //          driver.
  // Type:    Method.
  // Args:    None.
  // Return:  MIstatus::success - Functionality succeeded.
  //          MIstatus::failure - Functionality failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::InitClientIDEToMIDriver(void) const
  {
      // Put other IDE init functions here
      return InitClientIDEEclipse();
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: The IDE Eclipse when debugging locally expects "(gdb)\n" character
  //          sequence otherwise it refuses to communicate and times out. This should be
  //          sent to Eclipse before anything else.
  // Type:    Method.
  // Args:    None.
  // Return:  MIstatus::success - Functionality succeeded.
  //          MIstatus::failure - Functionality failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::InitClientIDEEclipse(void) const
  {
      return CMICmnStreamStdout::WritePrompt();
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Ask *this driver whether it found an executable in the MI Driver's list of
  //          arguments which to open and debug. If so instigate commands to set up a debug
  //          session for that executable.
  // Type:    Method.
  // Args:    None.
  // Return:  bool - True = True = Yes executable given as one of the parameters to the MI
  //                 Driver.
  //                 False = not found.
  // Throws:  None.
  //--
  bool
  CMIDriver::HaveExecutableFileNamePathOnCmdLine(void) const
  {
      return m_bHaveExecutableFileNamePathOnCmdLine;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Retrieve from *this driver executable file name path to start a debug session
  //          with (if present see HaveExecutableFileNamePathOnCmdLine()).
  // Type:    Method.
  // Args:    None.
  // Return:  CMIUtilString & - Executeable file name path or empty string.
  // Throws:  None.
  //--
  const CMIUtilString &
  CMIDriver::GetExecutableFileNamePathOnCmdLine(void) const
  {
      return m_strCmdLineArgExecuteableFileNamePath;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Execute commands (by injecting them into the stdin line queue container) and
  //          other code to set up the MI Driver such that is can take the executable
  //          argument passed on the command and create a debug session for it.
  // Type:    Method.
  // Args:    None.
  // Return:  MIstatus::success - Functionality succeeded.
  //          MIstatus::failure - Functionality failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::LocalDebugSessionStartupExecuteCommands(void)
  {
      const CMIUtilString strCmd(CMIUtilString::Format("-file-exec-and-symbols \"%s\"", m_strCmdLineArgExecuteableFileNamePath.AddSlashes().c_str()));
      bool bOk = CMICmnStreamStdout::TextToStdout(strCmd);
      bOk = bOk && InterpretCommand(strCmd);
      bOk = bOk && CMICmnStreamStdout::WritePrompt();
      return bOk;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Set the MI Driver into "its debugging an executable passed as an argument"
  //          mode as against running via a client like Eclipse.
  // Type:    Method.
  // Args:    None.
  // Return:  None.
  // Throws:  None.
  //--
  void
  CMIDriver::SetDriverDebuggingArgExecutable(void)
  {
      m_bDriverDebuggingArgExecutable = true;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Retrieve the MI Driver state indicating if it is operating in "its debugging
  //          an executable passed as an argument" mode as against running via a client
  //          like Eclipse.
  // Type:    Method.
  // Args:    None.
  // Return:  None.
  // Throws:  None.
  //--
  bool
  CMIDriver::IsDriverDebuggingArgExecutable(void) const
  {
      return m_bDriverDebuggingArgExecutable;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Execute commands from command source file in specified mode, and
  //          set exit-flag if needed.
  // Type:    Method.
  // Args:    vbAsyncMode       - (R) True = execute commands in asynchronous mode, false = otherwise.
  // Return:  MIstatus::success - Function succeeded.
  //          MIstatus::failure - Function failed.
  // Throws:  None.
  //--
  bool
  CMIDriver::ExecuteCommandFile(const bool vbAsyncMode)
  {
      std::ifstream ifsStartScript(m_strCmdLineArgCommandFileNamePath.c_str());
      if (!ifsStartScript.is_open())
      {
          const CMIUtilString errMsg(
              CMIUtilString::Format(MIRSRC(IDS_UTIL_FILE_ERR_OPENING_FILE_UNKNOWN), m_strCmdLineArgCommandFileNamePath.c_str()));
          SetErrorDescription(errMsg.c_str());
          const bool bForceExit = true;
          SetExitApplicationFlag(bForceExit);
          return MIstatus::failure;
      }
  
      // Switch lldb to synchronous mode
      CMICmnLLDBDebugSessionInfo &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance());
      const bool bAsyncSetting = rSessionInfo.GetDebugger().GetAsync();
      rSessionInfo.GetDebugger().SetAsync(vbAsyncMode);
  
      // Execute commands from file
      bool bOk = MIstatus::success;
      CMIUtilString strCommand;
      while (!m_bExitApp && std::getline(ifsStartScript, strCommand))
      {
          // Print command
          bOk = CMICmnStreamStdout::TextToStdout(strCommand);
  
          // Skip if it's a comment or empty line
          if (strCommand.empty() || strCommand[0] == '#')
              continue;
  
          // Execute if no error
          if (bOk)
          {
              CMIUtilThreadLock lock(rSessionInfo.GetSessionMutex());
              bOk = InterpretCommand(strCommand);
          }
  
          // Draw the prompt after command will be executed (if enabled)
          bOk = bOk && CMICmnStreamStdout::WritePrompt();
  
          // Exit if there is an error
          if (!bOk)
          {
              const bool bForceExit = true;
              SetExitApplicationFlag(bForceExit);
              break;
          }
  
          // Wait while the handler thread handles incoming events
          CMICmnLLDBDebugger::Instance().WaitForHandleEvent();
      }
  
      // Switch lldb back to initial mode
      rSessionInfo.GetDebugger().SetAsync(bAsyncSetting);
  
      return bOk;
  }
  
  //++ ------------------------------------------------------------------------------------
  // Details: Gets called when lldb-mi gets a signal. Stops the process if it was SIGINT.
  //
  // Type:    Method.
  // Args:    signal that was delivered
  // Return:  None.
  // Throws:  None.
  //--
  void
  CMIDriver::DeliverSignal(int signal)
  {
      if (signal == SIGINT && (m_eCurrentDriverState == eDriverState_RunningDebugging))
          InterpretCommand("-exec-interrupt");
  }
  