/*
* This file is part of Code::Blocks Studio, an open-source cross-platform IDE
* Copyright (C) 2003  Yiannis An. Mandravellos
*
* 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
*
* Contact e-mail: Yiannis An. Mandravellos <mandrav@codeblocks.org>
* Program URL   : http://www.codeblocks.org
*
* $Id: debuggergdb.cpp,v 1.62.2.1 2005/10/25 07:59:02 mandrav Exp $
* $Date: 2005/10/25 07:59:02 $
*/

#include <sdk.h>
#include <wx/txtstrm.h>
#include <wx/regex.h>
#include <wx/dialog.h>
#include <wx/msgdlg.h>
#include <wx/tokenzr.h>

#include <manager.h>
#include <configmanager.h>
#include <messagemanager.h>
#include <projectmanager.h>
#include <pluginmanager.h>
#include <editormanager.h>
#include <macrosmanager.h>
#include <projectbuildtarget.h>
#include <sdk_events.h>
#include <editarraystringdlg.h>
#include <compilerfactory.h>
#include <licenses.h>
#include <xtra_res.h>
#include <wx/xrc/xmlres.h>
#include <wx/fs_zip.h>

#include "debuggergdb.h"
#include "debuggeroptionsdlg.h"

#ifdef __WXMSW__
    #include <winbase.h> //For GetShortPathName()...only for windows systems
#endif

#define implement_debugger_toolbar

// valid debugger command constants
#define CMD_CONTINUE    1
#define CMD_STEP        2
#define CMD_STEPIN      3
#define CMD_STOP        4
#define CMD_BACKTRACE   5
#define CMD_DISASSEMBLE 6

#define GDB_PROMPT _T("(gdb)")

static const wxString g_EscapeChars = wxChar(26);

int idMenuDebug = XRCID("idDebuggerMenuDebug");
int idMenuRunToCursor = XRCID("idDebuggerMenuRunToCursor");
int idMenuNext = XRCID("idDebuggerMenuNext");
int idMenuStep = XRCID("idDebuggerMenuStep");
int idMenuStepOut = XRCID("idDebuggerMenuStepOut");
int idMenuStop = XRCID("idDebuggerMenuStop");
int idMenuContinue = XRCID("idDebuggerMenuContinue");
int idMenuToggleBreakpoint = XRCID("idDebuggerMenuToggleBreakpoint");
int idMenuSendCommandToGDB = XRCID("idDebuggerMenuSendCommandToGDB");
int idMenuAddSymbolFile = XRCID("idDebuggerMenuAddSymbolFile");
int idMenuCPU = XRCID("idDebuggerMenuCPU");
int idMenuBacktrace = XRCID("idDebuggerMenuBacktrace");
int idMenuEditWatches = XRCID("idDebuggerMenuEditWatches");

int idGDBProcess = wxNewId();
int idTimerPollDebugger = wxNewId();
int idMenuDebuggerAddWatch = wxNewId();

CB_IMPLEMENT_PLUGIN(DebuggerGDB);

BEGIN_EVENT_TABLE(DebuggerGDB, cbDebuggerPlugin)
	EVT_UPDATE_UI_RANGE(idMenuContinue, idMenuDebuggerAddWatch, DebuggerGDB::OnUpdateUI)
	// these are different because they are loaded from the XRC
	EVT_UPDATE_UI(XRCID("idDebuggerMenuDebug"), DebuggerGDB::OnUpdateUI)
	EVT_UPDATE_UI(XRCID("idDebuggerMenuRunToCursor"), DebuggerGDB::OnUpdateUI)
	EVT_UPDATE_UI(XRCID("idDebuggerMenuNext"), DebuggerGDB::OnUpdateUI)
	EVT_UPDATE_UI(XRCID("idDebuggerMenuStep"), DebuggerGDB::OnUpdateUI)
	EVT_UPDATE_UI(XRCID("idDebuggerMenuStepOut"), DebuggerGDB::OnUpdateUI)
	EVT_UPDATE_UI(XRCID("idDebuggerMenuStop"), DebuggerGDB::OnUpdateUI)

	EVT_MENU(idMenuDebug, DebuggerGDB::OnDebug)
	EVT_MENU(idMenuContinue, DebuggerGDB::OnContinue)
	EVT_MENU(idMenuNext, DebuggerGDB::OnNext)
	EVT_MENU(idMenuStep, DebuggerGDB::OnStep)
	EVT_MENU(idMenuStepOut, DebuggerGDB::OnStepOut)
	EVT_MENU(idMenuToggleBreakpoint, DebuggerGDB::OnToggleBreakpoint)
	EVT_MENU(idMenuRunToCursor, DebuggerGDB::OnRunToCursor)
	EVT_MENU(idMenuStop, DebuggerGDB::OnStop)
	EVT_MENU(idMenuSendCommandToGDB, DebuggerGDB::OnSendCommandToGDB)
	EVT_MENU(idMenuAddSymbolFile, DebuggerGDB::OnAddSymbolFile)
	EVT_MENU(idMenuBacktrace, DebuggerGDB::OnBacktrace)
	EVT_MENU(idMenuCPU, DebuggerGDB::OnDisassemble)
	EVT_MENU(idMenuEditWatches, DebuggerGDB::OnEditWatches)
    EVT_MENU(idMenuDebuggerAddWatch, DebuggerGDB::OnAddWatch)

	EVT_EDITOR_BREAKPOINT_ADDED(DebuggerGDB::OnBreakpointAdded)
	EVT_EDITOR_BREAKPOINT_DELETED(DebuggerGDB::OnBreakpointDeleted)
	EVT_EDITOR_TOOLTIP(DebuggerGDB::OnValueTooltip)

	EVT_PIPEDPROCESS_STDOUT(idGDBProcess, DebuggerGDB::OnGDBOutput)
	EVT_PIPEDPROCESS_STDERR(idGDBProcess, DebuggerGDB::OnGDBError)
	EVT_PIPEDPROCESS_TERMINATED(idGDBProcess, DebuggerGDB::OnGDBTerminated)

	EVT_IDLE(DebuggerGDB::OnIdle)
	EVT_TIMER(idTimerPollDebugger, DebuggerGDB::OnTimer)

	EVT_COMMAND(-1, cbCustom_WATCHES_CHANGED, DebuggerGDB::OnWatchesChanged)
END_EVENT_TABLE()

DebuggerGDB::DebuggerGDB()
	: m_pMenu(0L),
	m_pLog(0L),
	m_pDbgLog(0L),
	m_pProcess(0L),
	m_pTbar(0L),
	m_PageIndex(-1),
	m_DbgPageIndex(-1),
	m_ProgramIsStopped(true),
	m_pCompiler(0L),
	m_LastExitCode(0),
	m_TargetIndex(-1),
	m_Pid(0),
	m_EvalWin(0L),
	m_pTree(0L),
	m_NoDebugInfo(false),
	m_BreakOnEntry(false),
	m_HaltAtLine(0),
	m_HasDebugLog(false),
	m_StoppedOnSignal(false),
	m_pDisassembly(0),
	m_pBacktrace(0)
{
    Manager::Get()->Loadxrc(_T("/debugger_gdb.zip#zip:*.xrc"));

	m_PluginInfo.name = _T("DebuggerGDB");
	m_PluginInfo.title = _("GDB Debugger");
	m_PluginInfo.version = _T("0.1");
	m_PluginInfo.description = _("Plugin that interfaces the GNU GDB debugger.");
    m_PluginInfo.author = _T("Yiannis An. Mandravellos");
    m_PluginInfo.authorEmail = _T("info@codeblocks.org");
    m_PluginInfo.authorWebsite = _T("www.codeblocks.org");
	m_PluginInfo.thanksTo = _T("");
	m_PluginInfo.license = LICENSE_GPL;
	m_PluginInfo.hasConfigure = true;

	m_TimerPollDebugger.SetOwner(this, idTimerPollDebugger);

	ConfigManager::AddConfiguration(m_PluginInfo.title, _T("/debugger_gdb"));
}

void DebuggerGDB::OnAttach()
{
    MessageManager* msgMan = Manager::Get()->GetMessageManager();
    wxFont font(8, wxMODERN, wxNORMAL, wxNORMAL);
    m_pLog = new SimpleTextLog(msgMan, _("Debugger"));
    m_pLog->GetTextControl()->SetFont(font);
    m_PageIndex = msgMan->AddLog(m_pLog);
    // set log image
	wxBitmap bmp;
	wxString prefix = ConfigManager::Get()->Read(_T("data_path")) + _T("/images/");
    bmp.LoadFile(prefix + _T("misc_16x16.png"), wxBITMAP_TYPE_PNG);
    Manager::Get()->GetMessageManager()->SetLogImage(m_pLog, bmp);

    m_HasDebugLog = ConfigManager::Get()->Read(_T("debugger_gdb/debug_log"), (long int)0L);
    if (m_HasDebugLog)
    {
        m_pDbgLog = new SimpleTextLog(msgMan, m_PluginInfo.title + _(" (debug)"));
        m_pDbgLog->GetTextControl()->SetFont(font);
        m_DbgPageIndex = msgMan->AddLog(m_pDbgLog);
        // set log image
        bmp.LoadFile(prefix + _T("contents_16x16.png"), wxBITMAP_TYPE_PNG);
        Manager::Get()->GetMessageManager()->SetLogImage(m_pDbgLog, bmp);
    }

	if (!m_pTree)
		m_pTree = new DebuggerTree(this, Manager::Get()->GetNotebook());
}

void DebuggerGDB::OnRelease(bool appShutDown)
{
    if (m_pDisassembly)
        m_pDisassembly->Destroy();
    m_pDisassembly = 0;

    if (m_pBacktrace)
        m_pBacktrace->Destroy();
    m_pBacktrace = 0;

	if (m_pTree)
	{
		delete m_pTree;
		m_pTree = 0L;
	}

    //Close debug session when appShutDown
	CmdStop();

    if (Manager::Get()->GetMessageManager())
    {
        if (m_HasDebugLog)
            Manager::Get()->GetMessageManager()->DeletePage(m_DbgPageIndex);
        Manager::Get()->GetMessageManager()->DeletePage(m_PageIndex);
    }
}

DebuggerGDB::~DebuggerGDB()
{
}

int DebuggerGDB::Configure()
{
	DebuggerOptionsDlg dlg(Manager::Get()->GetAppWindow());
	int ret = dlg.ShowModal();

	bool needsRestart = ConfigManager::Get()->Read(_T("debugger_gdb/debug_log"), (long int)0L) != m_HasDebugLog;
	if (needsRestart)
        wxMessageBox(_("Code::Blocks needs to be restarted for the changes to take effect."), _("Information"), wxICON_INFORMATION);

	return ret;
}

void DebuggerGDB::BuildMenu(wxMenuBar* menuBar)
{
	if (!m_IsAttached)
		return;
    m_pMenu=Manager::Get()->LoadMenu(_T("debugger_menu"),true);

	// ok, now, where do we insert?
	// three possibilities here:
	// a) locate "Compile" menu and insert after it
	// b) locate "Project" menu and insert after it
	// c) if not found (?), insert at pos 5
	int finalPos = 5;
	int projcompMenuPos = menuBar->FindMenu(_("&Build"));
	if (projcompMenuPos == wxNOT_FOUND)
        projcompMenuPos = menuBar->FindMenu(_("&Compile"));

	if (projcompMenuPos != wxNOT_FOUND)
		finalPos = projcompMenuPos + 1;
	else
	{
		projcompMenuPos = menuBar->FindMenu(_("&Project"));
		if (projcompMenuPos != wxNOT_FOUND)
			finalPos = projcompMenuPos + 1;
	}
    menuBar->Insert(finalPos, m_pMenu, _("&Debug"));
}

void DebuggerGDB::BuildModuleMenu(const ModuleType type, wxMenu* menu, const wxString& arg)
{
	cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
	if (!m_IsAttached)
		return;
    // we 're only interested in editor menus
    // we 'll add a "debug watches" entry only when the debugger is running...
    if (type != mtEditorManager || !menu) return;
    if (!prj) return;
    // Insert toggle breakpoint
    menu->Insert(0,idMenuToggleBreakpoint, _("Toggle breakpoint"));
	// Insert Run to Cursor
	menu->Insert(1,idMenuRunToCursor, _("Run to cursor"));
	menu->Insert(2,wxID_SEPARATOR, _T("-"));

    if (!m_pProcess) return;
    // has to have a word under the caret...
    wxString w = GetEditorWordAtCaret();
    if (w.IsEmpty())
        return;

    wxString s;
    s.Printf(_("Watch '%s'"), w.c_str());
	menu->Insert(2, idMenuDebuggerAddWatch,  s);
}

bool DebuggerGDB::BuildToolBar(wxToolBar* toolBar)
{
	m_pTbar = toolBar;
    /* Loads toolbar using new Manager class functions */
#ifdef implement_debugger_toolbar
    if (!m_IsAttached || !toolBar)
		return false;
    wxString my_16x16=Manager::isToolBar16x16(toolBar) ? _T("_16x16") : _T("");
    Manager::AddonToolBar(toolBar,wxString(_T("debugger_toolbar"))+my_16x16);
    toolBar->Realize();
    return true;
#else
    return false;
#endif
}

void DebuggerGDB::DoWatches()
{
	wxString info;
	if (m_pProcess)
	{
        if (ConfigManager::Get()->Read(_T("debugger_gdb/watch_args"), 1))
            info << _T("Function Arguments = {") << GetInfoFor(_T("info args")) << _T("}") << _T('\n');
        if (ConfigManager::Get()->Read(_T("debugger_gdb/watch_locals"), 1))
    		info << _T("Local variables = {") << GetInfoFor(_T("info locals")) << _T("}") << _T('\n');
		for (unsigned int i = 0; i < m_pTree->GetWatches().GetCount(); ++i)
		{
			wxString watch = m_pTree->GetWatches()[i];
			info << watch << _T("{") << GetInfoFor(_T("output ") + watch) << _T("}") << _T('\n');
		}
	}
	else
	{
		// since no debugging session is active, we might as well show
		// the not-evaluated user-watches, just for feedback ;)
		for (unsigned int i = 0; i < m_pTree->GetWatches().GetCount(); ++i)
		{
			info << m_pTree->GetWatches()[i] << _T(',');
		}
	}
	//m_pLog->AddLog(info);
	m_pTree->BuildTree(info);
}

void DebuggerGDB::SetBreakpoints()
{
	SendCommand(_T("delete")); // clear all breakpoints

	cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
	if (prj)
	{
		for (int i = 0; i < prj->GetFilesCount(); ++i)
		{
			ProjectFile* pf = prj->GetFile(i);

			for (unsigned int x = 0; x < pf->breakpoints.GetCount(); ++x)
			{
				DebuggerBreakpoint* bp = pf->breakpoints[x];
				wxString filename = pf->file.GetFullName();
				wxString cmd;
				if (bp->enabled)
                {
					if (bp->func.IsEmpty())
					{
                        cmd << _T("break ") << filename << _T(":") << bp->line + 1;
                        SendCommand(cmd);
                    }
                    //GDB workaround
                    //Use function name if this is C++ constructor/destructor
					else
					{
						cmd << _T("break ") << bp->func;
						GetInfoFor(cmd);
					}
                    //end GDB workaround
				}
				//SendCommand(cmd);
			}
		}
	}
}

int DebuggerGDB::Debug()
{
	if (m_pProcess)
		return 1;
    m_NoDebugInfo = false;

	ProjectManager* prjMan = Manager::Get()->GetProjectManager();
	cbProject* project = prjMan->GetActiveProject();
	if (!project)
		return 2;

    MessageManager* msgMan = Manager::Get()->GetMessageManager();
    msgMan->SwitchTo(m_PageIndex);
	m_pLog->GetTextControl()->Clear();


    m_TargetIndex = project->GetActiveBuildTarget();
    msgMan->SwitchTo(m_PageIndex);
	msgMan->AppendLog(m_PageIndex, _("Selecting target: "));
	if (m_TargetIndex == -1)
	{
        m_TargetIndex = project->SelectTarget(m_TargetIndex);
        if (m_TargetIndex == -1)
        {
            msgMan->Log(m_PageIndex, _("canceled"));
            return 3;
        }
	}
	ProjectBuildTarget* target = project->GetBuildTarget(m_TargetIndex);
    if (target->GetTargetType() == ttCommandsOnly)
    {
        wxMessageBox(_("The selected target is only running pre/post build step commands\n"
                    "Can't debug such a target..."), _("Information"), wxICON_INFORMATION);
        msgMan->Log(m_PageIndex, _("aborted"));
        return 3;
    }
	msgMan->Log(m_PageIndex, target->GetTitle());

	Compiler* actualCompiler = CompilerFactory::Compilers[target ? target->GetCompilerIndex() : project->GetCompilerIndex()];
	if (!actualCompiler)
	{
		wxString msg;
		msg.Printf(_("This %s is configured to use an invalid debugger.\nThe operation failed..."), target ? _("target") : _("project"));
		wxMessageBox(msg, _("Error"), wxICON_ERROR);
		return 9;
	}

	if (actualCompiler->GetPrograms().DBG.IsEmpty() ||
        !wxFileExists(actualCompiler->GetMasterPath() + wxFileName::GetPathSeparator() + _T("bin") + wxFileName::GetPathSeparator() + actualCompiler->GetPrograms().DBG))
	{
        wxMessageBox(_("The debugger executable is not set.\n"
                    "To set it, go to \"Settings/Configure plugins/Compiler\", switch to the \"Programs\" tab\n"
                    "and select the debugger program."), _("Error"), wxICON_ERROR);
        msgMan->Log(m_PageIndex, _("Aborted"));
        return 4;
	}

    // make sure it is compiled
	PluginsArray plugins = Manager::Get()->GetPluginManager()->GetCompilerOffers();
	if (plugins.GetCount())
		m_pCompiler = (cbCompilerPlugin*)plugins[0];
	if (m_pCompiler)
	{
		msgMan->AppendLog(m_PageIndex, _("Compiling: "));
	    // is the compiler already running?
	    if (m_pCompiler->IsRunning())
        {
            msgMan->Log(m_PageIndex, _("compiler in use..."));
			msgMan->Log(m_PageIndex, _("Aborting debugging session"));
			return -1;
        }

		m_pCompiler->Compile(target);
		while (m_pCompiler->IsRunning())
			wxYield();
        msgMan->SwitchTo(m_PageIndex);
		if (m_pCompiler->GetExitCode() != 0)
		{
			msgMan->Log(m_PageIndex, _("failed"));
			msgMan->Log(m_PageIndex, _("Aborting debugging session"));
			return -1;
		}
		msgMan->Log(m_PageIndex, _("done"));
	}

	wxString cmdexe;
	cmdexe = actualCompiler->GetPrograms().DBG;
	cmdexe.Trim();
	cmdexe.Trim(true);
	if(cmdexe.IsEmpty())
    {
        msgMan->AppendLog(m_PageIndex,_("ERROR: You need to specify a debugger program in the compiler's settings."));
        #ifdef __WXMSW__
        msgMan->Log(m_PageIndex,_("\n(For MINGW compilers, it's 'gdb.exe' (without the quotes))"));
        #else
        msgMan->Log(m_PageIndex,_("\n(For GCC compilers, it's 'gdb' (without the quotes))"));
        #endif
        return -1;
    }

	wxString cmd;
	wxString sep = wxFileName::GetPathSeparator();
	cmd << actualCompiler->GetMasterPath() << sep << _T("bin") << sep << cmdexe << _T(" -nw -annotate=2 -silent");

	wxLogNull ln; // we perform our own error handling and logging
    m_pProcess = new PipedProcess((void**)&m_pProcess, this, idGDBProcess, true, project->GetBasePath());
	msgMan->AppendLog(m_PageIndex, _("Starting debugger: "));
//    msgMan->AppendLog(m_PageIndex, _(cmd));
    m_Pid = wxExecute(cmd, wxEXEC_ASYNC, m_pProcess);
//    m_Pid = m_pProcess->Launch(cmd);

    if (!m_Pid)
    {
		delete m_pProcess;
		m_pProcess = 0;
		msgMan->Log(m_PageIndex, _("failed"));
        return -1;
    }
    else if (!m_pProcess->GetOutputStream())
    {
		delete m_pProcess;
		m_pProcess = 0;
		msgMan->Log(m_PageIndex, _("failed (to get debugger's stdin)"));
        return -2;
    }
    else if (!m_pProcess->GetInputStream())
    {
		delete m_pProcess;
		m_pProcess = 0;
		msgMan->Log(m_PageIndex, _("failed (to get debugger's stdout)"));
        return -2;
    }
    else if (!m_pProcess->GetErrorStream())
    {
		delete m_pProcess;
		m_pProcess = 0;
		msgMan->Log(m_PageIndex, _("failed (to get debugger's stderr)"));
        return -2;
    }
	else
		msgMan->Log(m_PageIndex, _("done"));

	wxString out;
	m_TimerPollDebugger.Start(100);

    if (ConfigManager::Get()->Read(_T("debugger_gdb/add_other_search_dirs"), 0L))
    {
        // add as include dirs all open project base dirs
        ProjectsArray* projects = prjMan->GetProjects();
        for (unsigned int i = 0; i < projects->GetCount(); ++i)
        {
            cbProject* it = projects->Item(i);
            // skip if it's THE project (already added)
            if (it == project)
                continue;
            AddSourceDir(it->GetBasePath());
        }
    }
    // lastly, add THE project as source dir
	AddSourceDir(project->GetBasePath());

	cmd.Clear();
	switch (target->GetTargetType())
	{
		case ttExecutable:
		case ttConsoleOnly:
			// "-async" option is not really supported, at least under Win32, as far as I know
			out = UnixFilename(target->GetOutputFilename());
            Manager::Get()->GetMacrosManager()->ReplaceEnvVars(out); // apply env vars
			msgMan->Log(m_PageIndex, _("Adding file: %s"), out.c_str());
            ConvertToGDBDirectory(out);
			cmd << _T("file ") << out;
			// dll debugging steps:
			// gdb <hostapp>
			// (gdb) add-symbol-file <dllname> (and any other dlls the same way)
			SendCommand(cmd);
			break;

		case ttStaticLib:
		case ttDynamicLib:
			// check for hostapp
			if (target->GetHostApplication().IsEmpty())
			{
				wxMessageBox(_("You must select a host application to \"run\" a library..."));
				CmdStop();
				return 4;
			}
			out = UnixFilename(target->GetHostApplication());
            Manager::Get()->GetMacrosManager()->ReplaceEnvVars(out); // apply env vars
			msgMan->Log(m_PageIndex, _("Adding file: %s"), out.c_str());
			ConvertToGDBDirectory(out);
			cmd << _T("file ") << out;
			SendCommand(cmd);
			if (target->GetTargetType() == ttDynamicLib)
			{
				wxString symbols;
				out = UnixFilename(target->GetOutputFilename());
                Manager::Get()->GetMacrosManager()->ReplaceEnvVars(out); // apply env vars
				msgMan->Log(m_PageIndex, _("Adding symbol file: %s"), out.c_str());
                ConvertToGDBDirectory(out);
				symbols << _T("add-symbol-file ") << out;
				SendCommand(symbols);
			}
			break;

        default: break;
	}

	if (!target->GetExecutionParameters().IsEmpty())
		SendCommand(wxString(_T("set args ")) + target->GetExecutionParameters());

    // switch to output dir
	// wxFileName dir(target->GetOutputFilename());
	// wxString path = UnixFilename(dir.GetPath(wxPATH_GET_VOLUME));
    wxString path = UnixFilename(target->GetWorkingDir());
    if (!path.IsEmpty())
    {
        Manager::Get()->GetMacrosManager()->ReplaceEnvVars(path); // apply env vars
        cmd.Clear();
        ConvertToGDBDirectory(path);
        if (path != _T(".")) // avoid silly message "changing to ."
        {
            msgMan->Log(m_PageIndex, _("Changing directory to: %s"), path.c_str());
            cmd << _T("cd ") << path;
            SendCommand(cmd);
        }
    }

	SetBreakpoints();
	if (!m_Tbreak.IsEmpty())
	{
		SendCommand(m_Tbreak);
		m_Tbreak.Clear();
	}

    // send built-in init commands
	SendCommand(_T("set confirm off"));
#ifndef __WXMSW__
    SendCommand(_T("set disassembly-flavor att"));
#else
	if (target->GetTargetType() == ttConsoleOnly)
        SendCommand(_T("set new-console on"));
    SendCommand(_T("set disassembly-flavor intel"));
#endif

    // pass user init-commands
    wxString init = ConfigManager::Get()->Read(_T("debugger_gdb/init_commands"), _T(""));
    wxArrayString initCmds = GetArrayFromString(init, _T('\n'));
    for (unsigned int i = 0; i < initCmds.GetCount(); ++i)
    {
        SendCommand(initCmds[i]);
    }

    // finally, run the process
    if (m_BreakOnEntry)
    {
    	m_BreakOnEntry = false;
    	SendCommand(_T("start"));
	}
	else
        SendCommand(_T("run"));
	return 0;
}

void DebuggerGDB::StripQuotes(wxString& str)
{
	if (str.GetChar(0) == _T('\"') && str.GetChar(str.Length() - 1) == _T('\"'))
			str = str.Mid(1, str.Length() - 2);
}

void DebuggerGDB::AddSourceDir(const wxString& dir)
{
    if (dir.IsEmpty())
        return;
    wxString filename = dir;
    Manager::Get()->GetMacrosManager()->ReplaceEnvVars(filename); // apply env vars
    Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Adding source dir: %s"), filename.c_str());
    ConvertToGDBDirectory(filename, _T(""), false);
    SendCommand(_T("directory ") + filename);
}

void DebuggerGDB::ConvertToGDBFriendly(wxString& str)
{
    if (str.IsEmpty())
        return;

    str = UnixFilename(str);
    while (str.Replace(_T("\\"), _T("/")))
        ;
    while (str.Replace(_T("//"), _T("/")))
        ;
//    str.Replace("/", "//");
    if (str.Find(_T(' ')) != -1 && str.GetChar(0) != _T('"'))
        str = _T("\"") + str + _T("\"");
}

//if relative == false, try to leave as an absolute path
void DebuggerGDB::ConvertToGDBDirectory(wxString& str, wxString base, bool relative)
{
    if (str.IsEmpty())
        return;

	ConvertToGDBFriendly(str);
	ConvertToGDBFriendly(base);
	StripQuotes(str);
	StripQuotes(base);

	#ifdef __WXMSW__
		int ColonLocation = str.Find(_T(':'));
		wxChar buf[255];
		if(ColonLocation != -1)
		{
			//If can, get 8.3 name for path (Windows only)
			GetShortPathName(str.c_str(), buf, 255);
			str = buf;
		}
		else if(!base.IsEmpty() && str.GetChar(0) != _T('/'))
		{
			if(base.GetChar(base.Length()) == _T('/')) base = base.Mid(0, base.Length() - 2);
			while(!str.IsEmpty())
			{
				base += _T("/") + str.BeforeFirst(_T('/'));
				if(str.Find(_T('/')) != -1) str = str.AfterFirst(_T('/'));
				else str.Clear();
			}
			GetShortPathName(base.c_str(), buf, 255);
			str = buf;
		}

		if(ColonLocation == -1 || base.IsEmpty())
			relative = false;		//Can't do it
	#else
		if((str.GetChar(0) != _T('/') && str.GetChar(0) != _T('~')) || base.IsEmpty())
			relative = false;
	#endif

	if(relative)
	{
		#ifdef __WXMSW__
			if(str.Find(_T(':')) != -1) str = str.Mid(str.Find(_T(':')) + 2, str.Length());
			if(base.Find(_T(':')) != -1) base = base.Mid(base.Find(_T(':')) + 2, base.Length());
		#else
			if(str.GetChar(0) == _T('/')) str = str.Mid(1, str.Length());
			else if(str.GetChar(0) == _T('~')) str = str.Mid(2, str.Length());
			if(base.GetChar(0) == _T('/')) base = base.Mid(1, base.Length());
			else if(base.GetChar(0) == _T('~')) base = base.Mid(2, base.Length());
		#endif

		while(!base.IsEmpty() && !str.IsEmpty())
		{
			if(str.BeforeFirst(_T('/')) == base.BeforeFirst(_T('/')))
			{
				if(str.Find(_T('/')) == -1) str.Clear();
				else str = str.AfterFirst(_T('/'));

				if(base.Find(_T('/')) == -1) base.Clear();
				else base = base.AfterFirst(_T('/'));
			}
			else break;
		}
		while (!base.IsEmpty())
		{
			str = _T("../") + str;
			if(base.Find(_T('/')) == -1) base.Clear();
			else base = base.AfterFirst(_T('/'));
		}
	}
	ConvertToGDBFriendly(str);
}

void DebuggerGDB::SendCommand(const wxString& cmd)
{
    if (!m_pProcess || !m_ProgramIsStopped)
        return;
    if (m_HasDebugLog)
        Manager::Get()->GetMessageManager()->Log(m_DbgPageIndex, _T("> ") + cmd);
	m_pProcess->SendString(cmd);
}

wxString DebuggerGDB::GetNextOutputLine(bool useStdErr)
{
	if (!m_pProcess)
		return wxEmptyString;

	wxString bufferOut;

	wxInputStream* m_pOut = useStdErr ? m_pProcess->GetErrorStream() : m_pProcess->GetInputStream();
	while (useStdErr ? true : m_pProcess->IsInputOpened() &&
			useStdErr ? m_pProcess->IsErrorAvailable() : m_pProcess->IsInputAvailable() &&
			!m_pOut->Eof())
	{
		wxChar ch = m_pOut->GetC();
		if (ch == _T('\n') || ch == _T('\r'))
		{
			while (useStdErr ? m_pProcess->IsErrorAvailable() : m_pProcess->IsInputAvailable() &&
					!m_pOut->Eof() &&
					(m_pOut->Peek() == _T('\n') || m_pOut->Peek() == _T('\r'))
				)
				ch = m_pOut->GetC();
			break;
		}
		else
			bufferOut << ch;
	}

    if (m_HasDebugLog)
    {
        if (!bufferOut.IsEmpty())
            m_pDbgLog->AddLog(bufferOut);
    }
	return bufferOut;
}

wxString DebuggerGDB::GetNextOutputLineClean(bool useStdErr)
{
	wxString line = GetNextOutputLine(useStdErr);
	while (line.IsEmpty() || line.StartsWith(g_EscapeChars))
		line = GetNextOutputLine(useStdErr);

	return line;
}

void DebuggerGDB::RunCommand(int cmd)
{
    if (!m_pProcess || !m_ProgramIsStopped)
        return;

    switch (cmd)
    {
        case CMD_CONTINUE:
            ClearActiveMarkFromAllEditors();
            Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Continuing..."));
            SendCommand(_T("cont"));
            break;

        case CMD_STEP:
            ClearActiveMarkFromAllEditors();
            SendCommand(_T("next"));
            break;

        case CMD_STEPIN:
            ClearActiveMarkFromAllEditors();
            SendCommand(_T("step"));
            break;

        case CMD_STOP:
            ClearActiveMarkFromAllEditors();
            SendCommand(_T("quit"));
            break;

        case CMD_BACKTRACE:
//            Manager::Get()->GetMessageManager()->Log(m_PageIndex, "Running back-trace...");
            SendCommand(_T("bt"));
            break;

        case CMD_DISASSEMBLE:
        {
//            Manager::Get()->GetMessageManager()->Log(m_PageIndex, "Disassembling...");
            if (m_pDisassembly)
                m_pDisassembly->ClearRegisters();
            for (int i = 0; i < 16; ++i)
            {
                long int val = ReadRegisterValue(i);
                if (m_pDisassembly)
                    m_pDisassembly->AddRegisterValue(i, val);
//                Manager::Get()->GetMessageManager()->Log(m_PageIndex, "%s: '%s' (%d)", DisassemblyDlg::Registers[i].c_str(), token.c_str(), val);
            }
            SendCommand(_T("disassemble"));
            break;
        }

        default: break;
    }
}

long int DebuggerGDB::ReadRegisterValue(int idx)
{
    SendCommand(_T("info registers ") + DisassemblyDlg::Registers[idx]);
    wxString line;
    do
    {
        line = GetNextOutputLineClean();
    } while (!line.IsEmpty() && line.StartsWith(GDB_PROMPT));
    if (line.IsEmpty())
        return 0;
    // break up the string in its parts
    wxStringTokenizer tkz(line, wxT("\t"));
    wxString token;
    while (tkz.HasMoreTokens())
    {
        token = tkz.GetNextToken();
    }
    long int val;
    if (token.StartsWith(_T("0x")))
        token.ToLong(&val, 16);
    else
        token.ToLong(&val, 10);
    return val;
}

void DebuggerGDB::CmdDisassemble()
{
    if (!m_pDisassembly)
        m_pDisassembly = new DisassemblyDlg(Manager::Get()->GetAppWindow(), this);
    m_pDisassembly->Show();
    RunCommand(CMD_DISASSEMBLE);
}

void DebuggerGDB::CmdBacktrace()
{
    if (!m_pBacktrace)
        m_pBacktrace = new BacktraceDlg(Manager::Get()->GetAppWindow(), this);
    m_pBacktrace->Clear();
    m_pBacktrace->Show();
    RunCommand(CMD_BACKTRACE);
}

void DebuggerGDB::CmdContinue()
{
	SetBreakpoints();
	if (!m_Tbreak.IsEmpty())
	{
		SendCommand(m_Tbreak);
		m_Tbreak.Clear();
	}
    RunCommand(CMD_CONTINUE);
}

void DebuggerGDB::CmdNext()
{
    RunCommand(CMD_STEP);
}

void DebuggerGDB::CmdStep()
{
    RunCommand(CMD_STEPIN);
}

bool DebuggerGDB::Validate(const wxString& line, const char cb)
{
	bool bResult = false;

	int bep = line.Find(cb)+1;
	int scs = line.Find(_T('\''))+1;
	int sce = line.Find(_T('\''),true)+1;
	int dcs = line.Find(_T('"'))+1;
	int dce = line.Find(_T('"'),true)+1;
	//No single and double quote
	if(!scs && !sce && !dcs && !dce) bResult = true;
	//No single/double quote in pair
	if(!(sce-scs) && !(dce-dcs)) bResult = true;
	//Outside of single quote
	if((sce-scs) && ((bep < scs)||(bep >sce))) bResult = true;
	//Outside of double quote
	if((dce-dcs) && ((bep < dcs)||(bep >dce))) bResult = true;

	return bResult;
}

void DebuggerGDB::CmdStepOut()
{
	cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
	if (!ed) return;
	ProjectFile* pf = ed->GetProjectFile();
	if (!pf) return;
	wxString filename = pf->file.GetFullName(), lineBuf, cmd;
	cbStyledTextCtrl* stc = ed->GetControl();
	int line = m_HaltAtLine;
	lineBuf = stc->GetLine(line);

	unsigned int nLevel = 1;
	while(nLevel){
		 if ((lineBuf.Find(_T('{'))+1) && Validate(lineBuf, _T('{')) &&
			 (line > m_HaltAtLine)) nLevel++;
		 if ((lineBuf.Find(_T('}'))+1) && Validate(lineBuf, _T('}'))) nLevel--;
		 if (nLevel) lineBuf = stc->GetLine(++line);
	}
	if (line == stc->GetCurrentLine())
		CmdNext();
	else {
		cmd << _T("tbreak ") << filename << _T(":") << line+1;
		m_Tbreak = cmd;
		CmdContinue();
	}
}

void DebuggerGDB::CmdRunToCursor()
{
    cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
	if (!ed)
		return;

	ProjectFile* pf = ed->GetProjectFile();
	if (!pf)
		return;
	wxString cmd, filename = pf->file.GetFullName();
	cmd << _T("tbreak ") << filename << _T(":") << ed->GetControl()->GetCurrentLine()+1;
	m_Tbreak = cmd;
	if (m_pProcess)
	{
		CmdContinue();
	}
	else
	{
		Debug();
	}
}

void DebuggerGDB::CmdToggleBreakpoint()
{
	ClearActiveMarkFromAllEditors();
    cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
	if (!ed)
		return;
	ed->MarkerToggle(BREAKPOINT_MARKER);
//	SetBreakpoints();
}

void DebuggerGDB::CmdStop()
{
	if (m_pProcess && m_Pid)
	{
		if (m_ProgramIsStopped)
		{
            RunCommand(CMD_STOP);
            m_pProcess->CloseOutput();
		}
		else
		{
            m_pProcess->CloseOutput();
			wxKillError err = m_pProcess->Kill(m_Pid, wxSIGKILL);
			if (err == wxKILL_OK){
/*
				wxMessageBox(_("Debug session terminated!"),
					_("Debug"), wxOK | wxICON_EXCLAMATION);
*/
			}
			m_ProgramIsStopped = true;
		}
	}
}

void DebuggerGDB::ParseOutput(const wxString& output)
{
	wxString buffer = output;
	if (buffer.StartsWith(g_EscapeChars)) // ->->
	{
		buffer.Remove(0, 2); // remove ->->
        if (m_HasDebugLog)
            m_pDbgLog->AddLog(buffer); // write it in the full debugger log
		// Is the program running?
		if (buffer.Matches(_T("starting")))
			m_ProgramIsStopped = false;

		// Is the program stopped?
		else if (buffer.Matches(_T("stopped")))
		{
			bool already = m_ProgramIsStopped;
			m_ProgramIsStopped = true;
			if (!already)
			{
				DoWatches();
				// if stopped with a signal, force a backtrace
				if (m_StoppedOnSignal)
				{
                    CmdBacktrace();
                    m_StoppedOnSignal = false; // reset for next time
				}
			}
		}

		// Is the program exited?
		else if (buffer.StartsWith(_T("exited ")))
		{
			m_ProgramIsStopped = true;
			Manager::Get()->GetMessageManager()->Log(m_PageIndex, buffer);
			CmdStop();
		}

		// error
		else if (buffer.Matches(_T("error")))
		{
			Manager::Get()->GetMessageManager()->Log(m_PageIndex, buffer);
			//CmdStop();
		}
		else if (buffer.StartsWith(_T("error-begin")))
		{
            wxString error = GetNextOutputLineClean(true);
			Manager::Get()->GetMessageManager()->Log(m_PageIndex, error);
			if (error.StartsWith(_T("No symbol table is loaded.")))
                m_NoDebugInfo = true;
			//CmdStop();
		}

		// signal
		else if (buffer.Matches(_T("signal-name")))
		{
			BringAppToFront();
			wxString sig = GetNextOutputLineClean();
			Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Program received signal (%s)"), sig.c_str());
			m_StoppedOnSignal = true;
		}
		else if (buffer.Matches(_T("signal-string")))
		{
			wxString sig = GetNextOutputLineClean();
			Manager::Get()->GetMessageManager()->Log(m_PageIndex, sig);
		}

		// Stack-frame info
		else if (buffer.Matches(_T("frames-invalid")))
            m_CurrentFrame.Clear();
		else if (buffer.StartsWith(_T("frame-begin ")))
		{
            m_CurrentFrame.Clear();
            sscanf(buffer.mb_str(), "frame-begin %d %x", &m_CurrentFrame.number, &m_CurrentFrame.address);
            m_CurrentFrame.valid = true;
        }
		else if (buffer.Matches(_T("frame-function-name")))
		{
            m_CurrentFrame.function = GetNextOutputLineClean();
//			Manager::Get()->GetMessageManager()->Log(m_PageIndex, "m_FrameFunction=%s", m_FrameFunction.c_str());
        }
		else if (buffer.Matches(_T("frame-source-file")))
			m_CurrentFrame.file = GetNextOutputLineClean();
		else if (buffer.Matches(_T("frame-source-line")))
			m_CurrentFrame.line = GetNextOutputLineClean();
		else if (buffer.Matches(_T("frame-end")) && m_CurrentFrame.valid)
		{
            if (m_pBacktrace)
                m_pBacktrace->AddFrame(m_CurrentFrame);
//			Manager::Get()->GetMessageManager()->Log(m_PageIndex,
//                                                    _("Frame #%-2d [0x%8.8x]: %s (%s:%s)"),
//                                                    m_CurrentFrame.number,
//                                                    m_CurrentFrame.address,
//                                                    m_CurrentFrame.valid ? m_CurrentFrame.function.c_str() : "??",
//                                                    m_CurrentFrame.valid && !m_CurrentFrame.file.IsEmpty() ? m_CurrentFrame.file.c_str() : "??",
//                                                    m_CurrentFrame.valid && !m_CurrentFrame.line.IsEmpty() ? m_CurrentFrame.line.c_str() : "??");
		}

		// source d:/wx2.4/samples/exec/exec.cpp:753:22811:beg:0x403e39
		else if (buffer.StartsWith(_T("source ")))
		{
			Manager::Get()->GetMessageManager()->DebugLog(buffer);
			buffer.Remove(0, 7); // remove "source "

			if (!reSource.IsValid())
			#ifdef __WXMSW__
				reSource.Compile(_T("([A-Za-z]:)([ A-Za-z0-9_/\\.~-]*):([0-9]*):[0-9]*:beg:(0x[0-9A-Za-z]*)"));
			#else
				reSource.Compile(_T("([ A-Za-z0-9_/\\.~-]*):([0-9]*):[0-9]*:beg:(0x[0-9A-Za-z]*)"));
			#endif
			if ( reSource.Matches(buffer) )
			{
			#ifdef __WXMSW__
				wxString file = reSource.GetMatch(buffer, 1) + reSource.GetMatch(buffer, 2);
				wxString lineStr = reSource.GetMatch(buffer, 3);
				wxString addr = reSource.GetMatch(buffer, 4);
            #else
				wxString file = reSource.GetMatch(buffer, 1);
				wxString lineStr = reSource.GetMatch(buffer, 2);
				wxString addr = reSource.GetMatch(buffer, 3);
            #endif
                if (m_pDisassembly)
                {
                    long int val;
                    addr.ToLong(&val, 16);
                    m_pDisassembly->SetActiveAddress(val);
                    // update CPU registers
                    // NOTE: this hangs; another solution must be found...
//                    for (int i = 0; i < 16; ++i)
//                    {
//                        val = ReadRegisterValue(i);
//                        m_pDisassembly->SetRegisterValue(i, val);
//                    }
                }
				long int line;
				lineStr.ToLong(&line);
//				Manager::Get()->GetMessageManager()->DebugLog("file %s, line %ld", file.c_str(), line);
				SyncEditor(file, line);
				m_HaltAtLine = line-1;
				BringAppToFront();
			}
		}
	}
	else
	{
        if (buffer.StartsWith(_T("Dump of assembler code")))
        {
//            Manager::Get()->GetMessageManager()->Log(m_PageIndex,
//                                                    "Starting disassembly of %s (starting address: 0x%8.8x)",
//                                                    m_CurrentFrame.valid ? m_CurrentFrame.function.c_str() : "??",
//                                                    m_CurrentFrame.valid ? m_CurrentFrame.address : 0);
            if (m_pDisassembly)
                m_pDisassembly->Clear(m_CurrentFrame);
            //0x00403977 <_ZN7MyFrame11OnLocalTestER14wxCommandEvent+521>:	ret
            wxRegEx re(_T("(0x[0-9A-Za-z]+)[ \t]+<.*>:[ \t]+(.*)"));
            wxString tmp;
            do
            {
                tmp = GetNextOutputLine();
                if (tmp.Matches(_T("End of assembler dump.")))
                {
//                    Manager::Get()->GetMessageManager()->Log(m_PageIndex, "Disassembly end");
                    break;
                }
                if (re.Matches(tmp) && m_pDisassembly)
                {
                    long int val;
                    wxString addr = re.GetMatch(tmp, 1);
                    addr.ToLong(&val, 16);
                    m_pDisassembly->AddAssemblerLine(val, re.GetMatch(tmp, 2));
                }
//                    Manager::Get()->GetMessageManager()->Log(m_PageIndex, "%s: %s", re.GetMatch(tmp, 1).c_str(), re.GetMatch(tmp, 2).c_str());
            }
            while (!tmp.IsEmpty());
            if (m_pDisassembly)
                m_pDisassembly->Show(true);
        }
	}
//        Manager::Get()->GetMessageManager()->Log(m_PageIndex, buffer);
}

void DebuggerGDB::BringAppToFront()
{
	wxWindow* app = Manager::Get()->GetAppWindow();
	if (app)
		app->Raise();
}

void DebuggerGDB::ClearActiveMarkFromAllEditors()
{
	EditorManager* edMan = Manager::Get()->GetEditorManager();
	if (!edMan)
        return;
	for (int i = 0; i < edMan->GetEditorsCount(); ++i)
	{
        cbEditor* ed = edMan->GetBuiltinEditor(i);
        if (ed)
            ed->MarkLine(ACTIVE_LINE, -1);
	}
}

void DebuggerGDB::SyncEditor(const wxString& filename, int line)
{
	ClearActiveMarkFromAllEditors();
	cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
	if (project)
	{
        wxFileName fname(filename);
	    ProjectFile* f = project->GetFileByFilename(fname.GetFullPath(), false, true);
    	if (f)
        {
        	cbEditor* ed = Manager::Get()->GetEditorManager()->Open(f->file.GetFullPath());
            if (ed)
			{
				ed->SetProjectFile(f);
            	ed->Show(true);
				ed->GetControl()->GotoLine(line - 10); // make sure we can see some context...
				ed->GetControl()->GotoLine(line - 1);
				ed->MarkLine(ACTIVE_LINE, line - 1);
			}
        }
        else
        {
            // no such file in project; maybe in another open project?
        	cbEditor* ed = Manager::Get()->GetEditorManager()->Open(fname.GetFullPath());
            if (ed)
			{
            	ed->Show(true);
				ed->GetControl()->GotoLine(line - 10); // make sure we can see some context...
				ed->GetControl()->GotoLine(line - 1);
				ed->MarkLine(ACTIVE_LINE, line - 1);
			}
        }
	}
}

wxString DebuggerGDB::GetEditorWordAtCaret()
{
    cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    if (!ed)
        return _T("");
    int start = ed->GetControl()->WordStartPosition(ed->GetControl()->GetCurrentPos(), true);
    int end = ed->GetControl()->WordEndPosition(ed->GetControl()->GetCurrentPos(), true);
    return ed->GetControl()->GetTextRange(start, end);
}

// events

void DebuggerGDB::OnUpdateUI(wxUpdateUIEvent& event)
{
	cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
    cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    wxMenuBar* mbar = Manager::Get()->GetAppWindow()->GetMenuBar();
    if (mbar)
    {
        mbar->Enable(idMenuDebug, !m_pProcess && prj);
        mbar->Enable(idMenuContinue, m_pProcess && prj && m_ProgramIsStopped);
        mbar->Enable(idMenuNext, m_pProcess && prj && m_ProgramIsStopped);
        mbar->Enable(idMenuStep, prj && m_ProgramIsStopped);
        mbar->Enable(idMenuStepOut, m_pProcess && prj && m_ProgramIsStopped);
 		mbar->Enable(idMenuRunToCursor, prj && ed && m_ProgramIsStopped);
		mbar->Enable(idMenuToggleBreakpoint, prj && ed && m_ProgramIsStopped);
		mbar->Enable(idMenuSendCommandToGDB, m_pProcess && m_ProgramIsStopped);
 		mbar->Enable(idMenuAddSymbolFile, m_pProcess && m_ProgramIsStopped);
 		mbar->Enable(idMenuBacktrace, m_pProcess && m_ProgramIsStopped);
 		mbar->Enable(idMenuCPU, m_pProcess && m_ProgramIsStopped);
 		mbar->Enable(idMenuEditWatches, prj && m_ProgramIsStopped);
        mbar->Enable(idMenuStop, m_pProcess && prj);
	}

    #ifdef implement_debugger_toolbar
	wxToolBar* tbar = m_pTbar;//Manager::Get()->GetAppWindow()->GetToolBar();
    tbar->EnableTool(idMenuDebug, (!m_pProcess || m_ProgramIsStopped) && prj);
    tbar->EnableTool(idMenuRunToCursor, prj && ed && m_ProgramIsStopped);
    tbar->EnableTool(idMenuNext, m_pProcess && prj && m_ProgramIsStopped);
    tbar->EnableTool(idMenuStep, prj && m_ProgramIsStopped);
    tbar->EnableTool(idMenuStepOut, m_pProcess && prj && m_ProgramIsStopped);
    tbar->EnableTool(idMenuStop, m_pProcess && prj);
	#endif

    // allow other UpdateUI handlers to process this event
    // *very* important! don't forget it...
    event.Skip();
}

void DebuggerGDB::OnDebug(wxCommandEvent& event)
{
    if (!m_pProcess)
        Debug();
    else
    {
        if (m_ProgramIsStopped)
            CmdContinue();
    }
}

void DebuggerGDB::OnContinue(wxCommandEvent& event)
{
	CmdContinue();
}

void DebuggerGDB::OnNext(wxCommandEvent& event)
{
	CmdNext();
}

void DebuggerGDB::OnStep(wxCommandEvent& event)
{
	if (!m_pProcess)
	{
		m_BreakOnEntry = true;
		Debug();
	}
	else CmdStep();
}

void DebuggerGDB::OnStepOut(wxCommandEvent& event)
{
	CmdStepOut();
}

void DebuggerGDB::OnRunToCursor(wxCommandEvent& event)
{
	CmdRunToCursor();
}

void DebuggerGDB::OnToggleBreakpoint(wxCommandEvent& event)
{
	CmdToggleBreakpoint();
}

void DebuggerGDB::OnStop(wxCommandEvent& event)
{
	CmdStop();
}

void DebuggerGDB::OnSendCommandToGDB(wxCommandEvent& event)
{
	wxString cmd = wxGetTextFromUser(_("Enter command for GDB:"), _("Send command to GDB:"), m_LastCmd);
	if (cmd.IsEmpty())
		return;
	m_LastCmd = cmd;
//	Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Sending command \"%s\" to gdb:"), m_LastCmd.c_str());
//	SendCommand("set annotate 0");
	SendCommand(m_LastCmd);
//	SendCommand("set annotate 2");
}

wxString DebuggerGDB::GetInfoFor(const wxString& dbgCmd)
{
	if (!m_pProcess)
		return wxEmptyString;
	m_TimerPollDebugger.Stop();
	wxSafeYield();
	SendCommand(dbgCmd);
	wxString buf = GetNextOutputLine();
	wxString output;

	// delay until gdb starts sending output
	// (with 0.5 second timeout - approximately)
	int i = 0;
	while (buf.IsEmpty() && i < 500)
	{
		buf = GetNextOutputLine();
		#if wxVERSION_NUMBER < 2500
            wxUsleep(5);
        #else
            wxMilliSleep(5);
        #endif
		i += 5;
	}

	if (buf.IsEmpty())
		m_pLog->AddLog(_("Timeout expired waiting for info..."));

	// let's do business
	while (!buf.IsEmpty())
	{
		if (buf.StartsWith(g_EscapeChars))
		{
			// buffer holds an escaped output
			buf.Remove(0, 2); // clear it
			if (buf.Matches(_T("post-prompt")))
			{
				// our command has been parsed by gdb, so we start receiving data
				buf = GetNextOutputLine();
				while (1)
				{
					if (buf.StartsWith(g_EscapeChars))
					{
						// escaped output
						buf.Remove(0, 2);
						if (buf.Matches(_T("pre-prompt"))) // if we encounter this, we 're done
							break;
					}
					else if (!buf.IsEmpty())
					{
						// clean output
						output << buf;
						// append new-line when needed, to separate vars
						if (!buf.Matches(_T(" = ")) && buf.Find(_T('=')) != -1 && buf.GetChar(buf.Length() - 2) != _T(','))
							output << _T('\n');
					}
					buf = GetNextOutputLine();
				}
				break;
			}
		}
		buf = GetNextOutputLine();
	}
	m_TimerPollDebugger.Start(100);

	// loop until gdb finishes sending output
	// (with 0.5 second timeout - approximately)
	i = 0;
	while (!buf.IsEmpty() && i < 500)
	{
		buf = GetNextOutputLine();
        //GDB workaround
        //If overloaded C++ constructor/destructor, break on all.
		if (buf.StartsWith(g_EscapeChars))
		{
			buf.Remove(0,2);
			if (buf.Matches(_T("overload-choice")))
			{
				SendCommand(_T("1"));
			}
		}
        //end GDB workaround
		#if wxVERSION_NUMBER < 2500
            wxUsleep(5);
        #else
            wxMilliSleep(5);
        #endif
		i += 5;
	}

	return output;
}

void DebuggerGDB::OnAddSymbolFile(wxCommandEvent& event)
{
	wxString file = wxFileSelector(_("Choose file to read symbols from"),
									_T(""),
									_T(""),
									_T(""),
									_("Executables and libraries|*.exe;*.dll"),
									wxOPEN | wxFILE_MUST_EXIST);
	if (file.IsEmpty())
		return;
    Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Adding symbol file: %s"), file.c_str());
    ConvertToGDBDirectory(file);
	SendCommand(_T("add-symbol-file ") + file);
}

void DebuggerGDB::OnBacktrace(wxCommandEvent& event)
{
    CmdBacktrace();
}

void DebuggerGDB::OnDisassemble(wxCommandEvent& event)
{
    CmdDisassemble();
}

void DebuggerGDB::OnEditWatches(wxCommandEvent& event)
{
	wxArrayString watches = m_pTree->GetWatches();
	EditArrayStringDlg dlg(Manager::Get()->GetAppWindow(), watches);
	if (dlg.ShowModal() == wxID_OK)
	{
		m_pTree->SetWatches(watches);
	}
}

void DebuggerGDB::OnGDBOutput(wxCommandEvent& event)
{
	wxString msg = event.GetString();
	if (!msg.IsEmpty())
	{
		ParseOutput(msg);
	}
}

void DebuggerGDB::OnGDBError(wxCommandEvent& event)
{
	wxString msg = event.GetString();
	if (!msg.IsEmpty())
	{
		ParseOutput(msg);
	}
}

void DebuggerGDB::OnGDBTerminated(wxCommandEvent& event)
{
	m_TimerPollDebugger.Stop();
	m_LastExitCode = event.GetInt();
	//the process deletes itself
//	m_pProcess = 0L;

	ClearActiveMarkFromAllEditors();
	Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Debugger finished with status %d"), m_LastExitCode);

	if (m_NoDebugInfo)
	{
        wxMessageBox(_("This project/target has no debugging info."
                        "Please change this in the project's build options and retry..."),
                        _("Error"),
                        wxICON_STOP);
	}
}

void DebuggerGDB::OnBreakpointAdded(CodeBlocksEvent& event)
{
    if (!m_pProcess)
        return;

	cbEditor* ed = event.GetEditor();
	if (ed)
	{
		Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Breakpoint added: file %s, line %d"), ed->GetFilename().c_str(), event.GetInt() + 1);
/*
		ProjectFile* pf = ed->GetProjectFile();
		if (!pf)
			return;
        wxString filename = pf->relativeFilename;
        ConvertToGDBFriendly(filename);
//		Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("file %s"), filename.c_str());
		wxString cmd;
		cmd << "break " << filename << ":" << event.GetInt() + 1;
		SendCommand(cmd);
*/
	}
	else
		Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("OnBreakpointAdded(): No editor defined!"));
}

void DebuggerGDB::OnBreakpointDeleted(CodeBlocksEvent& event)
{
    if (!m_pProcess)
        return;

	cbEditor* ed = event.GetEditor();
	if (ed)
	{
		Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("Breakpoint deleted: file %s, line %d"), ed->GetFilename().c_str(), event.GetInt() + 1);
/*
		ProjectFile* pf = ed->GetProjectFile();
		if (!pf)
			return;
        wxString filename = pf->relativeFilename;
        ConvertToGDBFriendly(filename);
//		Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("file %s"), filename.c_str());
		wxString cmd;
		cmd << "clear " << filename << ":" << event.GetInt() + 1;
		SendCommand(cmd);
*/
	}
	else
		Manager::Get()->GetMessageManager()->Log(m_PageIndex, _("OnBreakpointDeleted(): No editor defined!"));
}

void DebuggerGDB::OnValueTooltip(CodeBlocksEvent& event)
{
	if (!m_pProcess || !m_ProgramIsStopped)
		return;
    if (!ConfigManager::Get()->Read(_T("debugger_gdb/eval_tooltip"), 0L))
        return;

	cbEditor* ed = event.GetEditor();
	if (!ed)
		return;

    int style = event.GetInt();
    if (style != wxSCI_C_DEFAULT && style != wxSCI_C_OPERATOR && style != wxSCI_C_IDENTIFIER)
        return;

	wxPoint pt;
	pt.x = event.GetX();
	pt.y = event.GetY();
	int pos = ed->GetControl()->PositionFromPoint(pt);
	int start = ed->GetControl()->WordStartPosition(pos, true);
	int end = ed->GetControl()->WordEndPosition(pos, true);
	wxString token;
	if (start >= ed->GetControl()->GetSelectionStart() &&
		end <= ed->GetControl()->GetSelectionEnd())
	{
		token = ed->GetControl()->GetSelectedText();
	}
	else
		token = ed->GetControl()->GetTextRange(start,end);

	if (!token.IsEmpty())
	{
		Manager::Get()->GetMessageManager()->AppendLog(m_PageIndex, _("Value of %s: "), token.c_str());
		pt = ed->GetControl()->PointFromPosition(start);
		pt = ed->GetControl()->ClientToScreen(pt);
		m_EvalRect.x = pt.x;
		m_EvalRect.y = pt.y;
		pt = ed->GetControl()->PointFromPosition(end);
		pt = ed->GetControl()->ClientToScreen(pt);
		m_EvalRect.width = pt.x - m_EvalRect.x;
		m_EvalRect.height = (pt.y + ed->GetControl()->GetCharHeight()) - m_EvalRect.y;
		m_LastEval = token;
		wxString tip;
        tip = GetInfoFor(_T("p ") + m_LastEval);
        int pos = tip.First(_T('\n')); // tip is e.g. "$1 = \n<value>"
        if (pos != -1)
            tip.Remove(0, pos + 1); // discard first line
		Manager::Get()->GetMessageManager()->AppendLog(m_PageIndex, _T("%s\n"), tip.c_str());
        tip = token + _T(" = ") + tip;
		if (m_EvalWin)
            m_EvalWin->Destroy();
		m_EvalWin = new wxTipWindow(Manager::Get()->GetAppWindow(), tip, 640, &m_EvalWin, &m_EvalRect);
	}
}

void DebuggerGDB::OnIdle(wxIdleEvent& event)
{
    if (m_pProcess && ((PipedProcess*)m_pProcess)->HasInput())
		event.RequestMore();
	else
		event.Skip();
}

void DebuggerGDB::OnTimer(wxTimerEvent& event)
{
    wxWakeUpIdle();
}

void DebuggerGDB::OnWatchesChanged(wxCommandEvent& event)
{
	DoWatches();
}

void DebuggerGDB::OnAddWatch(wxCommandEvent& event)
{
    m_pTree->AddWatch(GetEditorWordAtCaret());
}


syntax highlighted by Code2HTML, v. 0.9.1