|
|
|
/*
|
|
|
|
* Copyright Johannes Sixt
|
|
|
|
* This file is licensed under the GNU General Public License Version 2.
|
|
|
|
* See the file COPYING in the toplevel directory of the source directory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <tdeapplication.h>
|
|
|
|
#include <tdelocale.h> /* i18n */
|
|
|
|
#include <tdeconfig.h>
|
|
|
|
#include <tdemessagebox.h>
|
|
|
|
#include <kstatusbar.h>
|
|
|
|
#include <tdefiledialog.h>
|
|
|
|
#include <tqtabdialog.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqdragobject.h>
|
|
|
|
#include "mainwndbase.h"
|
|
|
|
#include "debugger.h"
|
|
|
|
#include "gdbdriver.h"
|
|
|
|
#include "xsldbgdriver.h"
|
|
|
|
#include "prefdebugger.h"
|
|
|
|
#include "prefmisc.h"
|
|
|
|
#include "ttywnd.h"
|
|
|
|
#include "commandids.h"
|
|
|
|
#ifdef HAVE_CONFIG
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
#include "mydebug.h"
|
|
|
|
#ifdef HAVE_SYS_STAT_H
|
|
|
|
#include <sys/stat.h> /* mknod(2) */
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_FCNTL_H
|
|
|
|
#include <fcntl.h> /* open(2) */
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h> /* getpid, unlink etc. */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define MAX_RECENT_FILES 4
|
|
|
|
|
|
|
|
WatchWindow::WatchWindow(TQWidget* parent, const char* name, WFlags f) :
|
|
|
|
TQWidget(parent, name, f),
|
|
|
|
m_watchEdit(this, "watch_edit"),
|
|
|
|
m_watchAdd(i18n(" Add "), this, "watch_add"),
|
|
|
|
m_watchDelete(i18n(" Del "), this, "watch_delete"),
|
|
|
|
m_watchVariables(this, i18n("Expression"), "watch_variables"),
|
|
|
|
m_watchV(this, 0),
|
|
|
|
m_watchH(0)
|
|
|
|
{
|
|
|
|
// setup the layout
|
|
|
|
m_watchAdd.setMinimumSize(m_watchAdd.sizeHint());
|
|
|
|
m_watchDelete.setMinimumSize(m_watchDelete.sizeHint());
|
|
|
|
m_watchV.addLayout(&m_watchH, 0);
|
|
|
|
m_watchV.addWidget(&m_watchVariables, 10);
|
|
|
|
m_watchH.addWidget(&m_watchEdit, 10);
|
|
|
|
m_watchH.addWidget(&m_watchAdd, 0);
|
|
|
|
m_watchH.addWidget(&m_watchDelete, 0);
|
|
|
|
|
|
|
|
connect(&m_watchEdit, SIGNAL(returnPressed()), SIGNAL(addWatch()));
|
|
|
|
connect(&m_watchAdd, SIGNAL(clicked()), SIGNAL(addWatch()));
|
|
|
|
connect(&m_watchDelete, SIGNAL(clicked()), SIGNAL(deleteWatch()));
|
|
|
|
connect(&m_watchVariables, SIGNAL(currentChanged(TQListViewItem*)),
|
|
|
|
SLOT(slotWatchHighlighted()));
|
|
|
|
|
|
|
|
m_watchVariables.installEventFilter(this);
|
|
|
|
setAcceptDrops(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
WatchWindow::~WatchWindow()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WatchWindow::eventFilter(TQObject*, TQEvent* ev)
|
|
|
|
{
|
|
|
|
if (ev->type() == TQEvent::KeyPress)
|
|
|
|
{
|
|
|
|
TQKeyEvent* kev = static_cast<TQKeyEvent*>(ev);
|
|
|
|
if (kev->key() == Key_Delete) {
|
|
|
|
emit deleteWatch();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WatchWindow::dragEnterEvent(TQDragEnterEvent* event)
|
|
|
|
{
|
|
|
|
event->accept(TQTextDrag::canDecode(event));
|
|
|
|
}
|
|
|
|
|
|
|
|
void WatchWindow::dropEvent(TQDropEvent* event)
|
|
|
|
{
|
|
|
|
TQString text;
|
|
|
|
if (TQTextDrag::decode(event, text))
|
|
|
|
{
|
|
|
|
// pick only the first line
|
|
|
|
text = text.stripWhiteSpace();
|
|
|
|
int pos = text.find('\n');
|
|
|
|
if (pos > 0)
|
|
|
|
text.truncate(pos);
|
|
|
|
text = text.stripWhiteSpace();
|
|
|
|
if (!text.isEmpty())
|
|
|
|
emit textDropped(text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// place the text of the hightlighted watch expr in the edit field
|
|
|
|
void WatchWindow::slotWatchHighlighted()
|
|
|
|
{
|
|
|
|
VarTree* expr = m_watchVariables.selectedItem();
|
|
|
|
TQString text = expr ? expr->computeExpr() : TQString();
|
|
|
|
m_watchEdit.setText(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char defaultTermCmdStr[] = "xterm -name kdbgio -title %T -e sh -c %C";
|
|
|
|
const char defaultSourceFilter[] = "*.c *.cc *.cpp *.c++ *.C *.CC";
|
|
|
|
const char defaultHeaderFilter[] = "*.h *.hh *.hpp *.h++";
|
|
|
|
|
|
|
|
|
|
|
|
DebuggerMainWndBase::DebuggerMainWndBase() :
|
|
|
|
m_outputTermCmdStr(defaultTermCmdStr),
|
|
|
|
m_outputTermProc(0),
|
|
|
|
m_ttyLevel(-1), /* no tty yet */
|
|
|
|
#ifdef GDB_TRANSCRIPT
|
|
|
|
m_transcriptFile(GDB_TRANSCRIPT),
|
|
|
|
#endif
|
|
|
|
m_popForeground(false),
|
|
|
|
m_backTimeout(1000),
|
|
|
|
m_tabWidth(0),
|
|
|
|
m_sourceFilter(defaultSourceFilter),
|
|
|
|
m_headerFilter(defaultHeaderFilter),
|
|
|
|
m_debugger(0)
|
|
|
|
{
|
|
|
|
m_statusActive = i18n("active");
|
|
|
|
}
|
|
|
|
|
|
|
|
DebuggerMainWndBase::~DebuggerMainWndBase()
|
|
|
|
{
|
|
|
|
delete m_debugger;
|
|
|
|
|
|
|
|
// if the output window is open, close it
|
|
|
|
if (m_outputTermProc != 0) {
|
|
|
|
m_outputTermProc->disconnect(); /* ignore signals */
|
|
|
|
m_outputTermProc->kill();
|
|
|
|
shutdownTermWindow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::setupDebugger(TQWidget* parent,
|
|
|
|
ExprWnd* localVars,
|
|
|
|
ExprWnd* watchVars,
|
|
|
|
TQListBox* backtrace)
|
|
|
|
{
|
|
|
|
m_debugger = new KDebugger(parent, localVars, watchVars, backtrace);
|
|
|
|
|
|
|
|
TQObject::connect(m_debugger, SIGNAL(updateStatusMessage()),
|
|
|
|
parent, SLOT(slotNewStatusMsg()));
|
|
|
|
TQObject::connect(m_debugger, SIGNAL(updateUI()),
|
|
|
|
parent, SLOT(updateUI()));
|
|
|
|
|
|
|
|
TQObject::connect(m_debugger, SIGNAL(breakpointsChanged()),
|
|
|
|
parent, SLOT(updateLineItems()));
|
|
|
|
|
|
|
|
TQObject::connect(m_debugger, SIGNAL(debuggerStarting()),
|
|
|
|
parent, SLOT(slotDebuggerStarting()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::setCoreFile(const TQString& corefile)
|
|
|
|
{
|
|
|
|
assert(m_debugger != 0);
|
|
|
|
m_debugger->useCoreFile(corefile, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::setRemoteDevice(const TQString& remoteDevice)
|
|
|
|
{
|
|
|
|
if (m_debugger != 0) {
|
|
|
|
m_debugger->setRemoteDevice(remoteDevice);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::overrideProgramArguments(const TQString& args)
|
|
|
|
{
|
|
|
|
assert(m_debugger != 0);
|
|
|
|
m_debugger->overrideProgramArguments(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::setTranscript(const TQString& name)
|
|
|
|
{
|
|
|
|
m_transcriptFile = name;
|
|
|
|
if (m_debugger != 0 && m_debugger->driver() != 0)
|
|
|
|
m_debugger->driver()->setLogFileName(m_transcriptFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char OutputWindowGroup[] = "OutputWindow";
|
|
|
|
const char TermCmdStr[] = "TermCmdStr";
|
|
|
|
const char KeepScript[] = "KeepScript";
|
|
|
|
const char DebuggerGroup[] = "Debugger";
|
|
|
|
const char DebuggerCmdStr[] = "DebuggerCmdStr";
|
|
|
|
const char PreferencesGroup[] = "Preferences";
|
|
|
|
const char PopForeground[] = "PopForeground";
|
|
|
|
const char BackTimeout[] = "BackTimeout";
|
|
|
|
const char TabWidth[] = "TabWidth";
|
|
|
|
const char FilesGroup[] = "Files";
|
|
|
|
const char SourceFileFilter[] = "SourceFileFilter";
|
|
|
|
const char HeaderFileFilter[] = "HeaderFileFilter";
|
|
|
|
const char GeneralGroup[] = "General";
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::saveSettings(TDEConfig* config)
|
|
|
|
{
|
|
|
|
if (m_debugger != 0) {
|
|
|
|
m_debugger->saveSettings(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
TDEConfigGroupSaver g(config, OutputWindowGroup);
|
|
|
|
config->writeEntry(TermCmdStr, m_outputTermCmdStr);
|
|
|
|
|
|
|
|
config->setGroup(DebuggerGroup);
|
|
|
|
config->writeEntry(DebuggerCmdStr, m_debuggerCmdStr);
|
|
|
|
|
|
|
|
config->setGroup(PreferencesGroup);
|
|
|
|
config->writeEntry(PopForeground, m_popForeground);
|
|
|
|
config->writeEntry(BackTimeout, m_backTimeout);
|
|
|
|
config->writeEntry(TabWidth, m_tabWidth);
|
|
|
|
config->writeEntry(SourceFileFilter, m_sourceFilter);
|
|
|
|
config->writeEntry(HeaderFileFilter, m_headerFilter);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::restoreSettings(TDEConfig* config)
|
|
|
|
{
|
|
|
|
if (m_debugger != 0) {
|
|
|
|
m_debugger->restoreSettings(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
TDEConfigGroupSaver g(config, OutputWindowGroup);
|
|
|
|
/*
|
|
|
|
* For debugging and emergency purposes, let the config file override
|
|
|
|
* the shell script that is used to keep the output window open. This
|
|
|
|
* string must have EXACTLY 1 %s sequence in it.
|
|
|
|
*/
|
|
|
|
setTerminalCmd(config->readEntry(TermCmdStr, defaultTermCmdStr));
|
|
|
|
m_outputTermKeepScript = config->readEntry(KeepScript);
|
|
|
|
|
|
|
|
config->setGroup(DebuggerGroup);
|
|
|
|
setDebuggerCmdStr(config->readEntry(DebuggerCmdStr));
|
|
|
|
|
|
|
|
config->setGroup(PreferencesGroup);
|
|
|
|
m_popForeground = config->readBoolEntry(PopForeground, false);
|
|
|
|
m_backTimeout = config->readNumEntry(BackTimeout, 1000);
|
|
|
|
m_tabWidth = config->readNumEntry(TabWidth, 0);
|
|
|
|
m_sourceFilter = config->readEntry(SourceFileFilter, m_sourceFilter);
|
|
|
|
m_headerFilter = config->readEntry(HeaderFileFilter, m_headerFilter);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::setAttachPid(const TQString& pid)
|
|
|
|
{
|
|
|
|
assert(m_debugger != 0);
|
|
|
|
m_debugger->setAttachPid(pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DebuggerMainWndBase::debugProgram(const TQString& executable,
|
|
|
|
TQString lang, TQWidget* parent)
|
|
|
|
{
|
|
|
|
assert(m_debugger != 0);
|
|
|
|
|
|
|
|
TRACE(TQString("trying language '%1'...").arg(lang));
|
|
|
|
DebuggerDriver* driver = driverFromLang(lang);
|
|
|
|
|
|
|
|
if (driver == 0)
|
|
|
|
{
|
|
|
|
// see if there is a language in the per-program config file
|
|
|
|
TQString configName = m_debugger->getConfigForExe(executable);
|
|
|
|
if (TQFile::exists(configName))
|
|
|
|
{
|
|
|
|
KSimpleConfig c(configName, true); // read-only
|
|
|
|
c.setGroup(GeneralGroup);
|
|
|
|
|
|
|
|
// Using "GDB" as default here is for backwards compatibility:
|
|
|
|
// The config file exists but doesn't have an entry,
|
|
|
|
// so it must have been created by an old version of KDbg
|
|
|
|
// that had only the GDB driver.
|
|
|
|
lang = c.readEntry(KDebugger::DriverNameEntry, "GDB");
|
|
|
|
|
|
|
|
TRACE(TQString("...bad, trying config driver %1...").arg(lang));
|
|
|
|
driver = driverFromLang(lang);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if (driver == 0)
|
|
|
|
{
|
|
|
|
TQString name = driverNameFromFile(executable);
|
|
|
|
|
|
|
|
TRACE(TQString("...no luck, trying %1 derived"
|
|
|
|
" from file contents").arg(name));
|
|
|
|
driver = driverFromLang(name);
|
|
|
|
}
|
|
|
|
if (driver == 0)
|
|
|
|
{
|
|
|
|
// oops
|
|
|
|
TQString msg = i18n("Don't know how to debug language `%1'");
|
|
|
|
KMessageBox::sorry(parent, msg.arg(lang));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
driver->setLogFileName(m_transcriptFile);
|
|
|
|
|
|
|
|
bool success = m_debugger->debugProgram(executable, driver);
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
delete driver;
|
|
|
|
|
|
|
|
TQString msg = i18n("Could not start the debugger process.\n"
|
|
|
|
"Please shut down KDbg and resolve the problem.");
|
|
|
|
KMessageBox::sorry(parent, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
// derive driver from language
|
|
|
|
DebuggerDriver* DebuggerMainWndBase::driverFromLang(TQString lang)
|
|
|
|
{
|
|
|
|
// lang is needed in all lowercase
|
|
|
|
lang = lang.lower();
|
|
|
|
|
|
|
|
// The following table relates languages and debugger drivers
|
|
|
|
static const struct L {
|
|
|
|
const char* shortest; // abbreviated to this is still unique
|
|
|
|
const char* full; // full name of language
|
|
|
|
int driver;
|
|
|
|
} langs[] = {
|
|
|
|
{ "c", "c++", 1 },
|
|
|
|
{ "f", "fortran", 1 },
|
|
|
|
{ "p", "python", 3 },
|
|
|
|
{ "x", "xslt", 2 },
|
|
|
|
// the following are actually driver names
|
|
|
|
{ "gdb", "gdb", 1 },
|
|
|
|
{ "xsldbg", "xsldbg", 2 },
|
|
|
|
};
|
|
|
|
const int N = sizeof(langs)/sizeof(langs[0]);
|
|
|
|
|
|
|
|
// lookup the language name
|
|
|
|
int driverID = 0;
|
|
|
|
for (int i = 0; i < N; i++)
|
|
|
|
{
|
|
|
|
const L& l = langs[i];
|
|
|
|
|
|
|
|
// shortest must match
|
|
|
|
if (!lang.startsWith(l.shortest))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// lang must not be longer than the full name, and it must match
|
|
|
|
if (TQString(l.full).startsWith(lang))
|
|
|
|
{
|
|
|
|
driverID = l.driver;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DebuggerDriver* driver = 0;
|
|
|
|
switch (driverID) {
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
GdbDriver* gdb = new GdbDriver;
|
|
|
|
gdb->setDefaultInvocation(m_debuggerCmdStr);
|
|
|
|
driver = gdb;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
driver = new XsldbgDriver;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// unknown language
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Try to guess the language to use from the contents of the file.
|
|
|
|
*/
|
|
|
|
TQString DebuggerMainWndBase::driverNameFromFile(const TQString& exe)
|
|
|
|
{
|
|
|
|
/* Inprecise but simple test to see if file is in XSLT language */
|
|
|
|
if (exe.right(4).lower() == ".xsl")
|
|
|
|
return "XSLT";
|
|
|
|
|
|
|
|
return "GDB";
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper that gets a file name (it only differs in the caption of the dialog)
|
|
|
|
TQString DebuggerMainWndBase::myGetFileName(TQString caption,
|
|
|
|
TQString dir, TQString filter,
|
|
|
|
TQWidget* parent)
|
|
|
|
{
|
|
|
|
TQString filename;
|
|
|
|
KFileDialog dlg(dir, filter, parent, "filedialog", true);
|
|
|
|
|
|
|
|
dlg.setCaption(caption);
|
|
|
|
|
|
|
|
if (dlg.exec() == TQDialog::Accepted)
|
|
|
|
filename = dlg.selectedFile();
|
|
|
|
|
|
|
|
return filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::newStatusMsg(KStatusBar* statusbar)
|
|
|
|
{
|
|
|
|
TQString msg = m_debugger->statusMessage();
|
|
|
|
statusbar->changeItem(msg, ID_STATUS_MSG);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::doGlobalOptions(TQWidget* parent)
|
|
|
|
{
|
|
|
|
TQTabDialog dlg(parent, "global_options", true);
|
|
|
|
TQString title = kapp->caption();
|
|
|
|
title += i18n(": Global options");
|
|
|
|
dlg.setCaption(title);
|
|
|
|
dlg.setCancelButton(i18n("Cancel"));
|
|
|
|
dlg.setOKButton(i18n("OK"));
|
|
|
|
|
|
|
|
PrefDebugger prefDebugger(&dlg);
|
|
|
|
prefDebugger.setDebuggerCmd(m_debuggerCmdStr.isEmpty() ?
|
|
|
|
GdbDriver::defaultGdb() : m_debuggerCmdStr);
|
|
|
|
prefDebugger.setTerminal(m_outputTermCmdStr);
|
|
|
|
|
|
|
|
PrefMisc prefMisc(&dlg);
|
|
|
|
prefMisc.setPopIntoForeground(m_popForeground);
|
|
|
|
prefMisc.setBackTimeout(m_backTimeout);
|
|
|
|
prefMisc.setTabWidth(m_tabWidth);
|
|
|
|
prefMisc.setSourceFilter(m_sourceFilter);
|
|
|
|
prefMisc.setHeaderFilter(m_headerFilter);
|
|
|
|
|
|
|
|
dlg.addTab(&prefDebugger, i18n("&Debugger"));
|
|
|
|
dlg.addTab(&prefMisc, i18n("&Miscellaneous"));
|
|
|
|
if (dlg.exec() == TQDialog::Accepted)
|
|
|
|
{
|
|
|
|
setDebuggerCmdStr(prefDebugger.debuggerCmd());
|
|
|
|
setTerminalCmd(prefDebugger.terminal());
|
|
|
|
m_popForeground = prefMisc.popIntoForeground();
|
|
|
|
m_backTimeout = prefMisc.backTimeout();
|
|
|
|
m_tabWidth = prefMisc.tabWidth();
|
|
|
|
m_sourceFilter = prefMisc.sourceFilter();
|
|
|
|
if (m_sourceFilter.isEmpty())
|
|
|
|
m_sourceFilter = defaultSourceFilter;
|
|
|
|
m_headerFilter = prefMisc.headerFilter();
|
|
|
|
if (m_headerFilter.isEmpty())
|
|
|
|
m_headerFilter = defaultHeaderFilter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char fifoNameBase[] = "/tmp/kdbgttywin%05d";
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We use the scope operator :: in this function, so that we don't
|
|
|
|
* accidentally use the wrong close() function (I've been bitten ;-),
|
|
|
|
* outch!) (We use it for all the libc functions, to be consistent...)
|
|
|
|
*/
|
|
|
|
TQString DebuggerMainWndBase::createOutputWindow()
|
|
|
|
{
|
|
|
|
// create a name for a fifo
|
|
|
|
TQString fifoName;
|
|
|
|
fifoName.sprintf(fifoNameBase, ::getpid());
|
|
|
|
|
|
|
|
// create a fifo that will pass in the tty name
|
|
|
|
TQFile::remove(fifoName); // remove remnants
|
|
|
|
#ifdef HAVE_MKFIFO
|
|
|
|
if (::mkfifo(fifoName.local8Bit(), S_IRUSR|S_IWUSR) < 0) {
|
|
|
|
// failed
|
|
|
|
TRACE("mkfifo " + fifoName + " failed");
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (::mknod(fifoName.local8Bit(), S_IFIFO | S_IRUSR|S_IWUSR, 0) < 0) {
|
|
|
|
// failed
|
|
|
|
TRACE("mknod " + fifoName + " failed");
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
m_outputTermProc = new TDEProcess;
|
|
|
|
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Spawn an xterm that in turn runs a shell script that passes us
|
|
|
|
* back the terminal name and then only sits and waits.
|
|
|
|
*/
|
|
|
|
static const char shellScriptFmt[] =
|
|
|
|
"tty>%s;"
|
|
|
|
"trap \"\" INT QUIT TSTP;" /* ignore various signals */
|
|
|
|
"exec<&-;exec>&-;" /* close stdin and stdout */
|
|
|
|
"while :;do sleep 3600;done";
|
|
|
|
// let config file override this script
|
|
|
|
TQString shellScript;
|
|
|
|
if (!m_outputTermKeepScript.isEmpty()) {
|
|
|
|
shellScript = m_outputTermKeepScript;
|
|
|
|
} else {
|
|
|
|
shellScript = shellScriptFmt;
|
|
|
|
}
|
|
|
|
|
|
|
|
shellScript.replace("%s", fifoName);
|
|
|
|
TRACE("output window script is " + shellScript);
|
|
|
|
|
|
|
|
TQString title = kapp->caption();
|
|
|
|
title += i18n(": Program output");
|
|
|
|
|
|
|
|
// parse the command line specified in the preferences
|
|
|
|
TQStringList cmdParts = TQStringList::split(' ', m_outputTermCmdStr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build the argv array. Thereby substitute special sequences:
|
|
|
|
*/
|
|
|
|
struct {
|
|
|
|
char seq[4];
|
|
|
|
TQString replace;
|
|
|
|
} substitute[] = {
|
|
|
|
{ "%T", title },
|
|
|
|
{ "%C", shellScript }
|
|
|
|
};
|
|
|
|
|
|
|
|
for (TQStringList::iterator i = cmdParts.begin(); i != cmdParts.end(); ++i)
|
|
|
|
{
|
|
|
|
TQString& str = *i;
|
|
|
|
for (int j = sizeof(substitute)/sizeof(substitute[0])-1; j >= 0; j--) {
|
|
|
|
int pos = str.find(substitute[j].seq);
|
|
|
|
if (pos >= 0) {
|
|
|
|
str.replace(pos, 2, substitute[j].replace);
|
|
|
|
break; /* substitute only one sequence */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*m_outputTermProc << str;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_outputTermProc->start())
|
|
|
|
{
|
|
|
|
TQString tty;
|
|
|
|
|
|
|
|
// read the ttyname from the fifo
|
|
|
|
TQFile f(fifoName);
|
|
|
|
if (f.open(IO_ReadOnly))
|
|
|
|
{
|
|
|
|
TQByteArray t = f.readAll();
|
|
|
|
tty = TQString::fromLocal8Bit(t, t.size());
|
|
|
|
f.close();
|
|
|
|
}
|
|
|
|
f.remove();
|
|
|
|
|
|
|
|
// remove whitespace
|
|
|
|
tty = tty.stripWhiteSpace();
|
|
|
|
TRACE("tty=" + tty);
|
|
|
|
return tty;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// error, could not start xterm
|
|
|
|
TRACE("fork failed for fifo " + fifoName);
|
|
|
|
TQFile::remove(fifoName);
|
|
|
|
shutdownTermWindow();
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::shutdownTermWindow()
|
|
|
|
{
|
|
|
|
delete m_outputTermProc;
|
|
|
|
m_outputTermProc = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::setTerminalCmd(const TQString& cmd)
|
|
|
|
{
|
|
|
|
m_outputTermCmdStr = cmd;
|
|
|
|
// revert to default if empty
|
|
|
|
if (m_outputTermCmdStr.isEmpty()) {
|
|
|
|
m_outputTermCmdStr = defaultTermCmdStr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::slotDebuggerStarting()
|
|
|
|
{
|
|
|
|
if (m_debugger == 0) /* paranoia check */
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (m_ttyLevel == m_debugger->ttyLevel())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// shut down terminal emulations we will not need
|
|
|
|
switch (m_ttyLevel) {
|
|
|
|
case KDebugger::ttySimpleOutputOnly:
|
|
|
|
ttyWindow()->deactivate();
|
|
|
|
break;
|
|
|
|
case KDebugger::ttyFull:
|
|
|
|
if (m_outputTermProc != 0) {
|
|
|
|
m_outputTermProc->kill();
|
|
|
|
// will be deleted in slot
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ttyLevel = m_debugger->ttyLevel();
|
|
|
|
|
|
|
|
TQString ttyName;
|
|
|
|
switch (m_ttyLevel) {
|
|
|
|
case KDebugger::ttySimpleOutputOnly:
|
|
|
|
ttyName = ttyWindow()->activate();
|
|
|
|
break;
|
|
|
|
case KDebugger::ttyFull:
|
|
|
|
if (m_outputTermProc == 0) {
|
|
|
|
// create an output window
|
|
|
|
ttyName = createOutputWindow();
|
|
|
|
TRACE(ttyName.isEmpty() ?
|
|
|
|
"createOuputWindow failed" : "successfully created output window");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_debugger->setTerminal(ttyName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DebuggerMainWndBase::setDebuggerCmdStr(const TQString& cmd)
|
|
|
|
{
|
|
|
|
m_debuggerCmdStr = cmd;
|
|
|
|
// make empty if it is the default
|
|
|
|
if (m_debuggerCmdStr == GdbDriver::defaultGdb()) {
|
|
|
|
m_debuggerCmdStr = TQString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "mainwndbase.moc"
|