nymea-zigbee/zigbee-cli/terminalcommander.cpp

204 lines
5.4 KiB
C++

#include "terminalcommander.h"
#include "loggingcategory.h"
#include <QStringList>
static char **commandCompletion(const char* text, int start, int end);
static char *commandCompletionGenerator(const char *text, int state);
static TerminalCommander *s_instance = nullptr;
TerminalCommander *TerminalCommander::instance()
{
if (!s_instance)
s_instance = new TerminalCommander();
return s_instance;
}
void TerminalCommander::destroy()
{
qCDebug(dcZigbee()) << "Shut down terminal commander. Have a nice day!";
stopProcess();
}
QList<TerminalCommand> TerminalCommander::commands() const
{
return m_commands;
}
void TerminalCommander::setCommands(QList<TerminalCommand> commands)
{
m_commands.append(commands);
}
TerminalCommander::TerminalCommander(QObject *parent) :
QThread(parent)
{
// Native terminal commands
m_commands.append(TerminalCommand("exit", "Exit this application"));
m_commands.append(TerminalCommand("quit", "Quit this application"));
m_commands.append(TerminalCommand("help", "Print this help message"));
}
TerminalCommander::~TerminalCommander()
{
destroy();
wait();
}
void TerminalCommander::printToTerminal(const QString &message)
{
rl_printf("%s", message.toStdString().data());
}
void TerminalCommander::stopProcess()
{
QMutexLocker locker(&m_mutex);
m_abort = true;
}
void TerminalCommander::run()
{
m_abort = false;
qCDebug(dcZigbee()) << "Command list:";
foreach (const TerminalCommand &command, m_commands) {
qCDebug(dcZigbee()) << " -->" << command;
}
rl_attempted_completion_function = commandCompletion;
rl_set_prompt(QString("%1[zigbee]%2 ").arg(terminalColorBoldGray).arg(terminalColorNormal).toStdString().data());
rl_redisplay();
rl_bind_key('\t',rl_complete);
rl_set_signals();
//rl_clear_signals();
rl_catch_signals = 1;
while (true) {
char *buffer = readline(QString("%1[zigbee]%2 ").arg(terminalColorBoldGray).arg(terminalColorNormal).toStdString().data());
rl_bind_key('\t',rl_complete);
if (buffer) {
if (!QString(buffer).isEmpty()) {
QStringList tokens = QString(buffer).split(" ");
// Check quit
if (tokens.count() == 1 && (tokens.first() == "quit" || tokens.first() == "exit")) {
qDebug() << "";
rl_on_new_line();
rl_replace_line("", 0);
rl_redisplay();
qCDebug(dcZigbee()) << "Terminal thread stopped";
free(buffer);
stopProcess();
return;
} else if (tokens.count() == 1 && (tokens.first() == "?" || tokens.first() == "help")) {
printHelpMessage();
add_history(buffer);
free(buffer);
} else {
emit commandReceived(tokens);
add_history(buffer);
free(buffer);
}
}
}
QMutexLocker locker(&m_mutex);
if (m_abort) {
//qDebug() << "";
//rl_on_new_line();
//rl_replace_line("", 0);
//rl_redisplay();
//qCDebug(dcZigbee()) << "Terminal thread stopped";
return;
}
}
msleep(10);
}
void TerminalCommander::rl_printf(const char *fmt, ...)
{
va_list args;
bool save_input;
char *saved_line;
int saved_point;
save_input = !RL_ISSTATE(RL_STATE_DONE);
if (save_input) {
saved_point = rl_point;
saved_line = rl_copy_text(0, rl_end);
rl_save_prompt();
rl_replace_line("", 0);
rl_redisplay();
}
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
if (save_input) {
rl_restore_prompt();
rl_replace_line(saved_line, 0);
rl_point = saved_point;
rl_redisplay();
free(saved_line);
}
}
void TerminalCommander::signalHandler(int status)
{
Q_UNUSED(status)
qCDebug(dcZigbee()) << "Terminal thread stopped";
TerminalCommander::instance()->stopProcess();
}
void TerminalCommander::printHelpMessage()
{
for (int i = 0; i < m_commands.count(); i++) {
QString command = QString("\t%1 %2").arg(m_commands.at(i).command(), -20).arg(m_commands.at(i).description());
rl_printf("%s\n",command.toStdString().data());
}
}
char **commandCompletion(const char *text, int start, int end)
{
Q_UNUSED(end)
rl_completion_append_character = '\0';
char **matches = (char **) NULL;
if (start == 0) {
matches = rl_completion_matches ((char *) text, &commandCompletionGenerator);
}
return matches;
}
char *commandCompletionGenerator(const char *text, int state)
{
//qCDebug(dcZigbee()) << text << state;
static int list_index, len;
const char *name;
if (!state) {
list_index = 0;
len = strlen(text);
}
while (list_index < TerminalCommander::instance()->commands().count() && (name = TerminalCommander::instance()->commands().at(list_index).command().toStdString().data())) {
//qCDebug(dcZigbee()) << " " << TerminalCommander::instance()->commands().at(list_index).command().toStdString().data();
list_index++;
if (strncmp (name, text, len) == 0) return strdup (name);
}
// If no names matched, then return NULL.
return ((char *) NULL);
}