aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/sqlitestudiocli/cli.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/sqlitestudiocli/cli.cpp')
-rw-r--r--SQLiteStudio3/sqlitestudiocli/cli.cpp321
1 files changed, 321 insertions, 0 deletions
diff --git a/SQLiteStudio3/sqlitestudiocli/cli.cpp b/SQLiteStudio3/sqlitestudiocli/cli.cpp
new file mode 100644
index 0000000..a663e84
--- /dev/null
+++ b/SQLiteStudio3/sqlitestudiocli/cli.cpp
@@ -0,0 +1,321 @@
+#include "cli.h"
+#include "services/config.h"
+#include "cli_config.h"
+#include "services/dbmanager.h"
+#include "commands/clicommandfactory.h"
+#include "commands/clicommand.h"
+#include "qio.h"
+#include "common/utils.h"
+#include "common/utils_sql.h"
+#include "climsghandler.h"
+#include "clicompleter.h"
+#include <QCoreApplication>
+#include <QThread>
+#include <QFile>
+#include <QSet>
+#include <QStringList>
+#include <QLibrary>
+
+#if defined(Q_OS_WIN32)
+#include "readline.h"
+#elif defined(Q_OS_UNIX)
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+CLI* CLI::instance = nullptr;
+
+CLI::CLI(QObject* parent) :
+ QObject(parent)
+{
+ setCurrentDb(nullptr);
+
+ using_history();
+
+#ifdef Q_OS_UNIX
+ history_base = 0; // for some reason this was set to 1 under Unix, making 1st history entry to be always ommited
+#endif
+
+
+ loadHistory();
+ CliCompleter::getInstance()->init(this);
+}
+
+CLI::~CLI()
+{
+}
+
+CLI* CLI::getInstance()
+{
+ if (!instance)
+ instance = new CLI();
+
+ return instance;
+}
+
+void CLI::start()
+{
+ thread = new QThread(this);
+
+ CliCommandFactory::init();
+
+ connect(thread, &QThread::started, this, &CLI::doWork);
+ connect(thread, &QThread::finished, this, &CLI::done);
+ this->moveToThread(thread);
+
+ if (!getCurrentDb()) // it could be set by openDbFile() from main().
+ {
+ Db* db = DBLIST->getByName(CFG_CLI.Console.DefaultDatabase.get());
+ if (db)
+ {
+ setCurrentDb(db);
+ }
+ else
+ {
+ QList<Db*> dbList = DBLIST->getDbList();
+ if (dbList.size() > 0)
+ setCurrentDb(dbList[0]);
+ else
+ setCurrentDb(nullptr);
+ }
+ }
+
+ qOut << QString("\n%1 (%2)\n------------------------\n\n").arg(QCoreApplication::applicationName()).arg(QCoreApplication::applicationVersion());
+ qOut.flush();
+
+ if (getCurrentDb())
+ qOut << tr("Current database: %1").arg(getCurrentDb()->getName()) << "\n";
+ else
+ qOut << tr("No current working database is set.") << "\n";
+
+ qOut << tr("Type %1 for help").arg(".help") << "\n\n";
+
+ qOut.flush();
+
+ thread->start();
+}
+
+void CLI::setCurrentDb(Db* db)
+{
+ currentDb = db;
+ if (db && !db->isOpen())
+ db->open();
+}
+
+Db* CLI::getCurrentDb() const
+{
+ return currentDb;
+}
+
+void CLI::exit()
+{
+ doExit = true;
+}
+
+QStringList CLI::getHistory() const
+{
+ QStringList cfgHistory;
+
+ int length = historyLength();
+
+ QString line;
+ HIST_ENTRY* entry = nullptr;
+ for (int i = 0; i < length; i++)
+ {
+ entry = history_get(i);
+ if (!entry)
+ {
+ qWarning() << "Null history entry for i =" << i;
+ continue;
+ }
+
+ line = QString::fromLocal8Bit(entry->line);
+ if (line.isEmpty())
+ continue;
+
+ cfgHistory << line;
+ }
+ return cfgHistory;
+}
+
+void CLI::println(const QString &msg)
+{
+ qOut << msg << "\n";
+ qOut.flush();
+}
+
+int CLI::historyLength() const
+{
+#if defined(Q_OS_WIN)
+ return history_length();
+#elif defined(Q_OS_UNIX)
+ return history_length;
+#endif
+}
+
+void CLI::waitForExecution()
+{
+ executionFinished = false;
+ while (!executionFinished)
+ {
+ qApp->processEvents();
+ QThread::usleep(20);
+ }
+}
+
+bool CLI::isComplete(const QString& contents) const
+{
+ if (contents.startsWith(CFG_CLI.Console.CommandPrefixChar.get()))
+ return true;
+
+ Dialect dialect = Dialect::Sqlite3;
+ if (getCurrentDb())
+ dialect = getCurrentDb()->getDialect();
+
+ bool complete = true;
+ splitQueries(contents, dialect, true, &complete);
+ return complete;
+}
+
+void CLI::loadHistory()
+{
+ foreach (const QString& line, CFG->getCliHistory())
+ {
+ if (!line.isEmpty())
+ add_history(line.toLocal8Bit().data());
+ }
+}
+
+void CLI::addHistory(const QString& text)
+{
+ if (text == lastHistoryEntry)
+ return;
+
+ CFG->addCliHistory(text);
+
+ add_history(text.toLocal8Bit().data());
+ if (historyLength() > CFG_CORE.Console.HistorySize.get())
+#ifdef Q_OS_OSX
+ free(remove_history(0));
+#else
+ free_history_entry(remove_history(0));
+#endif
+
+ lastHistoryEntry = text;
+}
+
+QString CLI::getLine() const
+{
+ return line;
+}
+
+void CLI::applyHistoryLimit()
+{
+ CFG->applyCliHistoryLimit();
+ while (historyLength() > CFG_CORE.Console.HistorySize.get())
+#ifdef Q_OS_OSX
+ free(remove_history(0));
+#else
+ free_history_entry(remove_history(0));
+#endif
+}
+
+void CLI::openDbFile(const QString& path)
+{
+ QString name = DBLIST->quickAddDb(path, QHash<QString,QVariant>());
+ if (name.isNull())
+ {
+ println(tr("Could not add database %1 to list.").arg(path));
+ return;
+ }
+ Db* db = DBLIST->getByName(name);
+ setCurrentDb(db);
+}
+
+void CLI::doWork()
+{
+ static const QString prompt = "%1>";
+
+ CliCommand* cliCommand = nullptr;
+ QString cmd;
+ QStringList cmdArgs;
+ QString cPrompt;
+ char *cline = nullptr;
+ while (!doExit)
+ {
+ line.clear();
+
+ while (!doExit && (line.isEmpty() || !isComplete(line)))
+ {
+ if (getCurrentDb())
+ {
+ cPrompt = getCurrentDb()->getName();
+ if (!getCurrentDb()->isOpen())
+ cPrompt += " ["+tr("closed")+"]";
+
+ cPrompt = prompt.arg(cPrompt);
+ }
+ else
+ cPrompt = prompt.arg("");
+
+ if (!line.isEmpty())
+ {
+ cPrompt = pad("->", -cPrompt.length(), ' ');
+ line += "\n";
+ }
+
+ cline = readline(cPrompt.toLocal8Bit().data());
+
+ line += cline;
+ free(cline);
+ }
+ addHistory(line);
+
+ if (line.startsWith(CFG_CLI.Console.CommandPrefixChar.get()))
+ {
+ cmdArgs = tokenizeArgs(line.mid(1));
+ cmd = cmdArgs.takeAt(0);
+ cliCommand = CliCommandFactory::getCommand(cmd);
+ if (!cliCommand)
+ {
+ println("No such command.");
+ continue;
+ }
+ }
+ else
+ {
+ cliCommand = CliCommandFactory::getCommand("query");
+ cmdArgs.clear();
+ cmdArgs << line;
+ }
+
+ cliCommand->setup(this);
+ if (!cliCommand->parseArgs(cmdArgs))
+ {
+ delete cliCommand;
+ continue;
+ }
+
+ cliCommand->moveToThread(qApp->thread());
+ emit execCommand(cliCommand);
+ waitForExecution();
+ }
+
+ thread->quit();
+}
+
+void CLI::done()
+{
+ qApp->exit();
+}
+
+void CLI::executionComplete()
+{
+ executionFinished = true;
+}
+
+void CLI::clearHistory()
+{
+ clear_history();
+ CFG->clearCliHistory();
+}