diff --git a/src/widgets/consolewidget.cpp b/src/widgets/consolewidget.cpp index 5b19144a..84bafc08 100644 --- a/src/widgets/consolewidget.cpp +++ b/src/widgets/consolewidget.cpp @@ -7,18 +7,20 @@ #include #include #include -//TODO: delete +#include +#include #include // TODO: Find a way to get to this without copying it here // source: libr/core/core.c:585.. -#define CMDS (sizeof (radare_argv)/sizeof(const char*)) -static const char *radare_argv[] = { +// remark: u.* is missing +static const QStringList radareArgs( +{ "?", "?v", "whereis", "which", "ls", "rm", "mkdir", "pwd", "cat", "less", "dH", "ds", "dso", "dsl", "dc", "dd", "dm", "db ", "db-", - "dp", "dr", "dcu", "dmd", "dmp", "dml", - "ec","ecs", "eco", + "dp", "dr", "dcu", "dmd", "dmp", "dml", + "ec", "ecs", "eco", "S", "S.", "S*", "S-", "S=", "Sa", "Sa-", "Sd", "Sl", "SSj", "Sr", "s", "s+", "s++", "s-", "s--", "s*", "sa", "sb", "sr", "!", "!!", @@ -27,10 +29,11 @@ static const char *radare_argv[] = { "V", "v", "aa", "ab", "af", "ar", "ag", "at", "a?", "ax", "ad", "ae", "aec", "aex", "aep", "aea", "aeA", "aes", "aeso", "aesu", "aesue", "aer", "aei", "aeim", "aef", - "aaa", "aac","aae", "aai", "aar", "aan", "aas", "aat", "aap", "aav", + "aaa", "aac", "aae", "aai", "aar", "aan", "aas", "aat", "aap", "aav", "af", "afa", "afan", "afc", "afC", "afi", "afb", "afbb", "afn", "afr", "afs", "af*", "afv", "afvn", "aga", "agc", "agd", "agl", "agfl", - "e", "et", "e-", "e*", "e!", "e?", "env ", + // see forbbidenArgs + //"e", "et", "e-", "e*", "e!", "e?", "env ", "i", "ii", "iI", "is", "iS", "iz", "q", "q!", "f", "fl", "fr", "f-", "f*", "fs", "fS", "fr", "fo", "f?", @@ -55,20 +58,52 @@ static const char *radare_argv[] = { "z/", "z/*", "zc", "zs", "zs+", "zs-", "zs-*", "zsr", - "#!pipe", - NULL -}; + "#!pipe" +}); + +static const QStringList forbiddenArgs({"e", "et", "e-", "e*", "e!", "e?", "env"}); + + +static const int invalidHistoryPos = -1; + + + +static bool isForbidden(const QString &input) +{ + static const QRegExp delimiters("[;&]"); + + + const QStringList &commands = input.split(delimiters, QString::SkipEmptyParts); + + for (const QString &command : commands) + { + const QString &trimmedCommand = command.trimmed(); + + if (forbiddenArgs.contains(trimmedCommand)) return true; + + for (const QString &arg : forbiddenArgs) + { + if (trimmedCommand.startsWith(arg + " ")) return true; + } + } + + return false; +} + ConsoleWidget::ConsoleWidget(QRCore *core, QWidget *parent) : QWidget(parent), ui(new Ui::ConsoleWidget), - core(core) + core(core), + debugOutputEnabled(true), + maxHistoryEntries(100), + lastHistoryPosition(invalidHistoryPos) { ui->setupUi(this); // Adjust console lineedit - ui->consoleInputLineEdit->setTextMargins(10, 0, 0, 0); + ui->inputLineEdit->setTextMargins(10, 0, 0, 0); /* ui->consoleOutputTextEdit->setFont(QFont("Monospace", 8)); @@ -77,16 +112,47 @@ ConsoleWidget::ConsoleWidget(QRCore *core, QWidget *parent) : */ // Adjust text margins of consoleOutputTextEdit - QTextDocument *console_docu = ui->consoleOutputTextEdit->document(); + QTextDocument *console_docu = ui->outputTextEdit->document(); console_docu->setDocumentMargin(10); // Fix output panel font - qhelpers::normalizeFont(ui->consoleOutputTextEdit); + qhelpers::normalizeFont(ui->outputTextEdit); + + QAction *action = new QAction(tr("Clear ouput"), ui->outputTextEdit); + connect(action, SIGNAL(triggered(bool)), ui->outputTextEdit, SLOT(clear())); + actions.append(action); + + action = new QAction(tr("Sync with core"), ui->outputTextEdit); + action->setCheckable(true); + connect(action, SIGNAL(toggled(bool)), this, SLOT(syncWithCoreToggled(bool))); + actions.append(action); + + // Completion + QCompleter *completer = new QCompleter(radareArgs, this); + completer->setMaxVisibleItems(20); + completer->setCaseSensitivity(Qt::CaseInsensitive); + completer->setFilterMode(Qt::MatchStartsWith); + + ui->inputLineEdit->setCompleter(completer); // Set console output context menu - ui->consoleOutputTextEdit->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->consoleOutputTextEdit, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(showConsoleContextMenu(const QPoint &))); + ui->outputTextEdit->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->outputTextEdit, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(showCustomContextMenu(const QPoint &))); + + // Esc clears inputLineEdit (like OmniBar) + QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), ui->inputLineEdit); + connect(clear_shortcut, SIGNAL(activated()), this, SLOT(clear())); + clear_shortcut->setContext(Qt::WidgetShortcut); + + // Up and down arrows show history + QShortcut *historyOnUp = new QShortcut(QKeySequence(Qt::Key_Up), ui->inputLineEdit); + connect(historyOnUp, SIGNAL(activated()), this, SLOT(historyPrev())); + historyOnUp->setContext(Qt::WidgetShortcut); + + QShortcut *historyOnDown = new QShortcut(QKeySequence(Qt::Key_Down), ui->inputLineEdit); + connect(historyOnDown, SIGNAL(activated()), this, SLOT(historyNext())); + historyOnDown->setContext(Qt::WidgetShortcut); } ConsoleWidget::~ConsoleWidget() @@ -96,94 +162,140 @@ ConsoleWidget::~ConsoleWidget() void ConsoleWidget::addOutput(const QString &msg) { - ui->consoleOutputTextEdit->appendPlainText(msg); - ui->consoleOutputTextEdit->verticalScrollBar()->setValue(ui->consoleOutputTextEdit->verticalScrollBar()->maximum()); + ui->outputTextEdit->appendPlainText(msg); + scrollOutputToEnd(); } void ConsoleWidget::addDebugOutput(const QString &msg) { - ui->consoleOutputTextEdit->appendHtml(" [DEBUG]:\t" + msg + ""); - ui->consoleOutputTextEdit->verticalScrollBar()->setValue(ui->consoleOutputTextEdit->verticalScrollBar()->maximum()); + if (debugOutputEnabled) + { + ui->outputTextEdit->appendHtml(" [DEBUG]:\t" + msg + ""); + scrollOutputToEnd(); + } } void ConsoleWidget::focusInputLineEdit() { - ui->consoleInputLineEdit->setFocus(); + ui->inputLineEdit->setFocus(); } -void ConsoleWidget::on_consoleInputLineEdit_returnPressed() +void ConsoleWidget::on_inputLineEdit_returnPressed() { - if (this->core) + QString input = ui->inputLineEdit->text(); + if (!input.isEmpty() && core != nullptr) { - QString input = ui->consoleInputLineEdit->text(); - ui->consoleOutputTextEdit->appendPlainText(this->core->cmd(input)); - ui->consoleOutputTextEdit->verticalScrollBar()->setValue(ui->consoleOutputTextEdit->verticalScrollBar()->maximum()); - // Add new command to history - QCompleter *completer = ui->consoleInputLineEdit->completer(); - if (completer != NULL) + if (!isForbidden(input)) { - QStringListModel *completerModel = (QStringListModel *)(completer->model()); - if (completerModel != NULL) - completerModel->setStringList(completerModel->stringList() << input); + ui->outputTextEdit->appendPlainText(this->core->cmd(input)); + scrollOutputToEnd(); + + historyAdd(input); + } + else + { + addDebugOutput(tr("command forbidden: ") + input); } - ui->consoleInputLineEdit->setText(""); + ui->inputLineEdit->clear(); } } -void ConsoleWidget::on_consoleExecButton_clicked() +void ConsoleWidget::on_execButton_clicked() { - on_consoleInputLineEdit_returnPressed(); + on_inputLineEdit_returnPressed(); } -void ConsoleWidget::showConsoleContextMenu(const QPoint &pt) +void ConsoleWidget::showCustomContextMenu(const QPoint &pt) { - // Set console output popup menu - QMenu *menu = ui->consoleOutputTextEdit->createStandardContextMenu(); - menu->clear(); -// TODO: -// menu->addAction(ui->actionClear_ConsoleOutput); -// menu->addAction(ui->actionConsoleSync_with_core); - //ui->actionConsoleSync_with_core->setChecked(true); - ui->consoleOutputTextEdit->setContextMenuPolicy(Qt::CustomContextMenu); - - menu->exec(ui->consoleOutputTextEdit->mapToGlobal(pt)); - delete menu; + QMenu *menu = new QMenu(ui->outputTextEdit); + menu->addActions(actions); + menu->exec(ui->outputTextEdit->mapToGlobal(pt)); + menu->deleteLater(); } -void ConsoleWidget::on_actionConsoleSync_with_core_triggered() +void ConsoleWidget::syncWithCoreToggled(bool checked) { -// TODO: -// if (ui->actionConsoleSync_with_core->isChecked()) -// { -// //Enable core syncronization -// } -// else -// { -// // Disable core sync -// } -} - -void ConsoleWidget::on_actionClear_ConsoleOutput_triggered() -{ - ui->consoleOutputTextEdit->clear(); -} - - -void ConsoleWidget::on_showHistoToolButton_clicked() -{ - QCompleter *completer = ui->consoleInputLineEdit->completer(); - if (completer == NULL) - return; - - if (ui->showHistoToolButton->isChecked()) + if (checked) { - completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); - // Uhm... shouldn't it be called always? - completer->complete(); + //Enable core syncronization } else { - completer->setCompletionMode(QCompleter::PopupCompletion); + // Disable core sync } } + +void ConsoleWidget::historyNext() +{ + if (!history.isEmpty()) + { + if (lastHistoryPosition > invalidHistoryPos) + { + if (lastHistoryPosition >= history.size()) + { + lastHistoryPosition = history.size() -1 ; + } + + --lastHistoryPosition; + + if (lastHistoryPosition >= 0) + { + ui->inputLineEdit->setText(history.at(lastHistoryPosition)); + } + else + { + ui->inputLineEdit->clear(); + } + + + } + } +} + +void ConsoleWidget::historyPrev() +{ + if (!history.isEmpty()) + { + if (lastHistoryPosition >= history.size() -1) + { + lastHistoryPosition = history.size() - 2; + } + + ui->inputLineEdit->setText(history.at(++lastHistoryPosition)); + } +} + +void ConsoleWidget::clear() +{ + ui->inputLineEdit->clear(); + + invalidateHistoryPosition(); + + // Close the potential shown completer popup + ui->inputLineEdit->clearFocus(); + ui->inputLineEdit->setFocus(); +} + +void ConsoleWidget::scrollOutputToEnd() +{ + const int maxValue = ui->outputTextEdit->verticalScrollBar()->maximum(); + ui->outputTextEdit->verticalScrollBar()->setValue(maxValue); +} + +void ConsoleWidget::historyAdd(const QString &input) +{ + if (history.size() + 1 > maxHistoryEntries) + { + history.removeLast(); + } + + history.prepend(input); + + invalidateHistoryPosition(); +} + +void ConsoleWidget::invalidateHistoryPosition() +{ + lastHistoryPosition = invalidHistoryPos; +} diff --git a/src/widgets/consolewidget.h b/src/widgets/consolewidget.h index edfd5075..2da6fa38 100644 --- a/src/widgets/consolewidget.h +++ b/src/widgets/consolewidget.h @@ -5,11 +5,14 @@ class QRCore; +class QAction; + namespace Ui { class ConsoleWidget; } + class ConsoleWidget : public QWidget { Q_OBJECT @@ -21,24 +24,39 @@ public: void addOutput(const QString &msg); void addDebugOutput(const QString &msg); + void setDebugOutputEnabled(bool enabled) { debugOutputEnabled = enabled; } + + void setMaxHistoryEntries(int max) { maxHistoryEntries = max; } + public slots: void focusInputLineEdit(); private slots: - void on_consoleInputLineEdit_returnPressed(); + void on_inputLineEdit_returnPressed(); - void on_consoleExecButton_clicked(); + void on_execButton_clicked(); - void showConsoleContextMenu(const QPoint &pt); + void showCustomContextMenu(const QPoint &pt); - void on_actionClear_ConsoleOutput_triggered(); - void on_actionConsoleSync_with_core_triggered(); + void syncWithCoreToggled(bool checked); - void on_showHistoToolButton_clicked(); + void historyNext(); + void historyPrev(); + + void clear(); private: + void scrollOutputToEnd(); + void historyAdd(const QString &input); + void invalidateHistoryPosition(); + Ui::ConsoleWidget *ui; QRCore *core; + QList actions; + bool debugOutputEnabled; + int maxHistoryEntries; + int lastHistoryPosition; + QStringList history; }; #endif // CONSOLEWIDGET_H diff --git a/src/widgets/consolewidget.ui b/src/widgets/consolewidget.ui index 458cbcb7..d5894893 100644 --- a/src/widgets/consolewidget.ui +++ b/src/widgets/consolewidget.ui @@ -30,7 +30,7 @@ 0 - + 0 @@ -81,7 +81,7 @@ 3 - + 12 @@ -90,9 +90,6 @@ border-top: 1px solid rgb(255, 255, 255); - - - false @@ -105,54 +102,7 @@ - - - false - - - - 16777215 - 18 - - - - Show commands history - - - background-color: white; - - - ... - - - true - - - true - - - Qt::DownArrow - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - - - + Execute command