summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Seigo <aseigo@kde.org>2015-12-23 10:47:32 +0100
committerAaron Seigo <aseigo@kde.org>2015-12-23 10:47:32 +0100
commit255d73af197faf8437343abc10bd98cca2057a1e (patch)
treef16bdbdda08a450235230781eec558303c6442a1
parentb259728a4f63e022526ef86e6b5d6c62d9938d13 (diff)
downloadsink-255d73af197faf8437343abc10bd98cca2057a1e.tar.gz
sink-255d73af197faf8437343abc10bd98cca2057a1e.zip
initial import of the new cli app
-rw-r--r--CMakeLists.txt3
-rw-r--r--akonadi2_cli/CMakeLists.txt16
-rw-r--r--akonadi2_cli/main.cpp71
-rw-r--r--akonadi2_cli/module.cpp195
-rw-r--r--akonadi2_cli/module.h61
-rw-r--r--akonadi2_cli/modules/exit/exit.cpp39
-rw-r--r--akonadi2_cli/modules/exit/exit.h37
-rw-r--r--akonadi2_cli/modules/help/help.cpp80
-rw-r--r--akonadi2_cli/modules/help/help.h37
-rw-r--r--akonadi2_cli/repl/repl.cpp78
-rw-r--r--akonadi2_cli/repl/repl.h34
-rw-r--r--akonadi2_cli/repl/replStates.cpp155
-rw-r--r--akonadi2_cli/repl/replStates.h85
-rw-r--r--akonadi2_cli/state.cpp28
-rw-r--r--akonadi2_cli/state.h29
-rw-r--r--cmake/modules/FindReadline.cmake47
16 files changed, 995 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c480ddd..d92f830 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,4 +57,7 @@ add_subdirectory(examples)
57# some tests 57# some tests
58add_subdirectory(tests) 58add_subdirectory(tests)
59 59
60# cli
61add_subdirectory(akonadi2_cli)
62
60feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) 63feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/akonadi2_cli/CMakeLists.txt b/akonadi2_cli/CMakeLists.txt
new file mode 100644
index 0000000..b5bdb46
--- /dev/null
+++ b/akonadi2_cli/CMakeLists.txt
@@ -0,0 +1,16 @@
1project(akonadi2_cli)
2
3find_package(Readline REQUIRED)
4
5
6set(akonadi2_SRCS
7 main.cpp
8 module.cpp
9 modules/exit/exit.cpp
10 modules/help/help.cpp
11 repl/repl.cpp
12 repl/replStates.cpp
13 state.cpp)
14
15add_executable(${PROJECT_NAME} ${akonadi2_SRCS})
16
diff --git a/akonadi2_cli/main.cpp b/akonadi2_cli/main.cpp
new file mode 100644
index 0000000..1a036d0
--- /dev/null
+++ b/akonadi2_cli/main.cpp
@@ -0,0 +1,71 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include <unistd.h>
21
22#include <QCoreApplication>
23#include <QDebug>
24
25#include "module.h"
26// #include "jsonlistener.h"
27#include "repl/repl.h"
28
29/*
30 * modes of operation:
31 *
32 * 1. called with no commands: start the REPL and listen for JSON on stin
33 * 2. called with -: listen for JSON on stdin
34 * 3. called with commands: try to match to syntx
35 */
36
37int main(int argc, char *argv[])
38{
39 // load all modules
40 Module::loadModules();
41
42 const bool interactive = isatty(fileno(stdin));
43 const bool startRepl = (argc == 1) && interactive;
44 //TODO: make a json command parse cause that would be awesomesauce
45 const bool startJsonListener = !startRepl &&
46 (argc == 2 && qstrcmp(argv[1], "-") == 0);
47 qDebug() << "state at startup is" << interactive << startRepl << startJsonListener;
48
49 QCoreApplication app(argc, argv);
50 app.setApplicationName("funq");
51
52 if (startRepl || startJsonListener) {
53 if (startRepl) {
54 Repl *repl = new Repl;
55 QObject::connect(repl, &QStateMachine::finished,
56 repl, &QObject::deleteLater);
57 QObject::connect(repl, &QStateMachine::finished,
58 &app, &QCoreApplication::quit);
59 }
60
61 if (startJsonListener) {
62// JsonListener listener(syntax);
63 }
64
65 return app.exec();
66 }
67
68 QStringList commands = app.arguments();
69 commands.removeFirst();
70 return Module::run(commands);
71}
diff --git a/akonadi2_cli/module.cpp b/akonadi2_cli/module.cpp
new file mode 100644
index 0000000..0b83d98
--- /dev/null
+++ b/akonadi2_cli/module.cpp
@@ -0,0 +1,195 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "module.h"
21
22#include <QCoreApplication>
23#include <QDebug>
24
25// TODO: needs a proper registry; making "core" modules plugins is
26// almost certainly overkill, but this is not the way either
27#include "modules/exit/exit.h"
28#include "modules/help/help.h"
29
30QList<Module> Module::s_modules;
31State Module::s_state;
32
33Module::Syntax::Syntax()
34{
35}
36
37Module::Syntax::Syntax(const QString &k, std::function<bool(const QStringList &, State &)> l, const QString &helpText, bool e)
38 : keyword(k),
39 lambda(l),
40 help(helpText),
41 eventDriven(e)
42{
43}
44
45Module::Module()
46{
47}
48
49void Module::loadModules()
50{
51 addModule(CLI::Exit());
52 addModule(CLI::Help());
53}
54
55void Module::addModule(const Module &module)
56{
57 s_modules.append(module);
58}
59
60QList<Module> Module::modules()
61{
62 return s_modules;
63}
64
65Module::Command Module::match(const QStringList &commands)
66{
67 Command command;
68 for (const Module &module: s_modules) {
69 command = module.matches(commands);
70 if (command.first) {
71 break;
72 }
73 }
74
75 return command;
76}
77
78Module::Syntax Module::syntax() const
79{
80 return m_syntax;
81}
82
83void Module::setSyntax(const Syntax &syntax)
84{
85 m_syntax = syntax;
86}
87
88bool Module::run(const QStringList &commands)
89{
90 Command command = match(commands);
91 if (command.first && command.first->lambda) {
92 bool rv = command.first->lambda(command.second, s_state);
93 if (rv && command.first->eventDriven) {
94 return QCoreApplication::instance()->exec();
95 }
96
97 return rv;
98 }
99
100 return false;
101}
102
103Module::Command Module::matches(const QStringList &commandLine) const
104{
105 if (commandLine.isEmpty()) {
106 return Command();
107 }
108
109 QStringListIterator commandLineIt(commandLine);
110
111 if (commandLineIt.next() != m_syntax.keyword) {
112 return Command();
113 }
114
115 QListIterator<Syntax> syntaxIt(m_syntax.children);
116 const Syntax *syntax = &m_syntax;
117 QStringList tailCommands;
118 while (commandLineIt.hasNext() && syntaxIt.hasNext()) {
119 const QString word = commandLineIt.next();
120 while (syntaxIt.hasNext()) {
121 const Syntax &child = syntaxIt.next();
122 if (word == child.keyword) {
123 syntax = &child;
124 syntaxIt = child.children;
125 }
126 }
127
128 if (!syntaxIt.hasNext()) {
129 tailCommands << word;
130 break;
131 }
132 }
133
134 if (syntax && syntax->lambda) {
135 while (commandLineIt.hasNext()) {
136 tailCommands << commandLineIt.next();
137 }
138
139 return std::make_pair(syntax, tailCommands);
140 }
141
142 return Command();
143}
144
145QVector<Module::Syntax> Module::nearestSyntax(const QStringList &words, const QString &fragment)
146{
147 QVector<Module::Syntax> matches;
148
149 //qDebug() << "words are" << words;
150 if (words.isEmpty()) {
151 for (const Module &module: s_modules) {
152 if (module.syntax().keyword.startsWith(fragment)) {
153 matches.push_back(module.syntax());
154 }
155 }
156 } else {
157 QStringListIterator wordIt(words);
158 QString word = wordIt.next();
159 Syntax lastFullSyntax;
160
161 for (const Module &module: s_modules) {
162 if (module.syntax().keyword == word) {
163 lastFullSyntax = module.syntax();
164 QListIterator<Syntax> syntaxIt(module.syntax().children);
165 while (wordIt.hasNext()) {
166 word = wordIt.next();
167 while (syntaxIt.hasNext()) {
168 const Syntax &child = syntaxIt.next();
169 if (word == child.keyword) {
170 lastFullSyntax = child;
171 syntaxIt = child.children;
172 }
173 }
174 }
175
176 break;
177 }
178 }
179
180 //qDebug() << "exiting with" << lastFullSyntax.keyword << words.last();
181 if (lastFullSyntax.keyword == words.last()) {
182 QListIterator<Syntax> syntaxIt(lastFullSyntax.children);
183 while (syntaxIt.hasNext()) {
184 Syntax syntax = syntaxIt.next();
185 if (fragment.isEmpty() || syntax.keyword.startsWith(fragment)) {
186 matches.push_back(syntax);
187 }
188 }
189 }
190 }
191
192 return matches;
193}
194
195
diff --git a/akonadi2_cli/module.h b/akonadi2_cli/module.h
new file mode 100644
index 0000000..4f426b8
--- /dev/null
+++ b/akonadi2_cli/module.h
@@ -0,0 +1,61 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include "state.h"
23
24#include <QStringList>
25#include <QVector>
26
27class Module
28{
29public:
30 struct Syntax
31 {
32 Syntax();
33 Syntax(const QString &keyword, std::function<bool(const QStringList &, State &)> lambda = std::function<bool(const QStringList &, State &)>(), const QString &helpText = QString(), bool eventDriven = false);
34 QString keyword;
35 std::function<bool(const QStringList &, State &)> lambda;
36 QList<Syntax> children;
37 QString help;
38 bool eventDriven;
39 };
40
41 typedef std::pair<const Syntax *, QStringList> Command;
42
43 static void addModule(const Module &module);
44 static QList<Module> modules();
45 static Command match(const QStringList &commands);
46 static bool run(const QStringList &commands);
47 static void loadModules();
48 static QVector<Syntax>nearestSyntax(const QStringList &words, const QString &fragment);
49
50 Module();
51 Module::Syntax syntax() const;
52 void setSyntax(const Syntax &syntax);
53
54private:
55 Command matches(const QStringList &commands) const;
56
57 Syntax m_syntax;
58 static QList<Module> s_modules;
59 static State s_state;
60};
61
diff --git a/akonadi2_cli/modules/exit/exit.cpp b/akonadi2_cli/modules/exit/exit.cpp
new file mode 100644
index 0000000..64828be
--- /dev/null
+++ b/akonadi2_cli/modules/exit/exit.cpp
@@ -0,0 +1,39 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "exit.h"
21
22#include <QObject>
23
24namespace CLI
25{
26
27Exit::Exit()
28{
29 setSyntax(Syntax("exit", &Exit::exit, QObject::tr("Exits the application. Ctrl-d also works!")));
30}
31
32bool Exit::exit(const QStringList &, State &)
33{
34 ::exit(0);
35 return true;
36}
37
38} // namespace CLI
39
diff --git a/akonadi2_cli/modules/exit/exit.h b/akonadi2_cli/modules/exit/exit.h
new file mode 100644
index 0000000..5ed4174
--- /dev/null
+++ b/akonadi2_cli/modules/exit/exit.h
@@ -0,0 +1,37 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include "module.h"
23
24namespace CLI
25{
26
27class Exit : public Module
28{
29public:
30 Exit();
31
32private:
33 static bool exit(const QStringList &commands, State &state);
34};
35
36} // namespace CLI
37
diff --git a/akonadi2_cli/modules/help/help.cpp b/akonadi2_cli/modules/help/help.cpp
new file mode 100644
index 0000000..aaff6fb
--- /dev/null
+++ b/akonadi2_cli/modules/help/help.cpp
@@ -0,0 +1,80 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "help.h"
21
22#include <QObject>
23#include <QSet>
24#include <QTextStream>
25
26#include "module.h"
27
28namespace CLI
29{
30
31Help::Help()
32{
33 Syntax topLevel = Syntax(QObject::tr("help"), &Help::showHelp, QObject::tr("Print command information: help [command]"));
34 setSyntax(topLevel);
35}
36
37bool Help::showHelp(const QStringList &commands, State &)
38{
39 Module::Command command = Module::match(commands);
40 QTextStream stream(stdout);
41 if (commands.isEmpty()) {
42 stream << QObject::tr("Welcome to the Akonadi2 command line tool!") << "\n";
43 stream << QObject::tr("Top-level commands:") << "\n";
44 QSet<QString> sorted;
45 for (auto module: Module::modules()) {
46 sorted.insert(module.syntax().keyword);
47 }
48
49 for (auto keyword: sorted) {
50 stream << "\t" << keyword << "\n";
51 }
52 } else if (const Syntax *syntax = command.first) {
53 //TODO: get parent!
54 stream << QObject::tr("Command `%1`").arg(syntax->keyword);
55
56 if (!syntax->help.isEmpty()) {
57 stream << ": " << syntax->help;
58 }
59 stream << "\n";
60
61 if (!syntax->children.isEmpty()) {
62 stream << "\tSub-commands:\n";
63 QSet<QString> sorted;
64 for (auto childSyntax: syntax->children) {
65 sorted.insert(childSyntax.keyword);
66 }
67
68 for (auto keyword: sorted) {
69 stream << "\t" << keyword << "\n";
70 }
71 }
72 } else {
73 stream << "Unknown command: " << commands.join(" ") << "\n";
74 }
75
76 return true;
77}
78
79} // namespace CLI
80
diff --git a/akonadi2_cli/modules/help/help.h b/akonadi2_cli/modules/help/help.h
new file mode 100644
index 0000000..df1cfc2
--- /dev/null
+++ b/akonadi2_cli/modules/help/help.h
@@ -0,0 +1,37 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include "module.h"
23
24namespace CLI
25{
26
27class Help : public Module
28{
29public:
30 Help();
31
32private:
33 static bool showHelp(const QStringList &commands, State &state);
34};
35
36} // namespace CLI
37
diff --git a/akonadi2_cli/repl/repl.cpp b/akonadi2_cli/repl/repl.cpp
new file mode 100644
index 0000000..395661e
--- /dev/null
+++ b/akonadi2_cli/repl/repl.cpp
@@ -0,0 +1,78 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "repl.h"
21
22#include <readline/history.h>
23
24#include <QDir>
25#include <QFile>
26#include <QFinalState>
27#include <QStandardPaths>
28
29#include "replStates.h"
30
31Repl::Repl(QObject *parent)
32 : QStateMachine(parent)
33{
34 // readline history setup
35 using_history();
36 read_history(commandHistoryPath().toLocal8Bit());
37
38 // create all states
39 ReadState *read = new ReadState(this);
40 UnfinishedReadState *unfinishedRead = new UnfinishedReadState(this);
41 EvalState *eval = new EvalState(this);
42 PrintState *print = new PrintState(this);
43 QFinalState *final = new QFinalState(this);
44
45 // connect the transitions
46 read->addTransition(read, SIGNAL(command(QString)), eval);
47 read->addTransition(read, SIGNAL(exitRequested()), final);
48
49 unfinishedRead->addTransition(unfinishedRead, SIGNAL(command(QString)), eval);
50 unfinishedRead->addTransition(unfinishedRead, SIGNAL(exitRequested()), final);
51
52 eval->addTransition(eval, SIGNAL(completed()), read);
53 eval->addTransition(eval, SIGNAL(continueInput()), unfinishedRead);
54 eval->addTransition(eval, SIGNAL(output(QString)), print);
55
56 print->addTransition(print, SIGNAL(completed()), eval);
57
58 setInitialState(read);
59 start();
60}
61
62Repl::~Repl()
63{
64 // readline history writing
65 write_history(commandHistoryPath().toLocal8Bit());
66}
67
68QString Repl::commandHistoryPath()
69{
70 const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
71 if (!QFile::exists(path)) {
72 QDir dir;
73 dir.mkpath(path);
74 }
75 return path + "/repl_history";
76}
77
78#include "moc_repl.cpp"
diff --git a/akonadi2_cli/repl/repl.h b/akonadi2_cli/repl/repl.h
new file mode 100644
index 0000000..b76c66b
--- /dev/null
+++ b/akonadi2_cli/repl/repl.h
@@ -0,0 +1,34 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include <QStateMachine>
23
24class Repl : public QStateMachine
25{
26 Q_OBJECT
27
28public:
29 Repl(QObject *parent = 0);
30 ~Repl();
31
32private:
33 static QString commandHistoryPath();
34};
diff --git a/akonadi2_cli/repl/replStates.cpp b/akonadi2_cli/repl/replStates.cpp
new file mode 100644
index 0000000..e87dd5f
--- /dev/null
+++ b/akonadi2_cli/repl/replStates.cpp
@@ -0,0 +1,155 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "replStates.h"
21
22#include <unistd.h>
23#include <iostream>
24
25#include <readline/readline.h>
26#include <readline/history.h>
27
28#include <QDebug>
29#include <QEvent>
30#include <QStateMachine>
31
32#include "module.h"
33
34static char *akonadi2_cli_next_tab_complete_match(const char *text, int state);
35static char ** akonadi2_cli_tab_completion(const char *text, int start, int end);
36
37ReadState::ReadState(QState *parent)
38 : QState(parent)
39{
40 rl_completion_entry_function = akonadi2_cli_next_tab_complete_match;
41 rl_attempted_completion_function = akonadi2_cli_tab_completion;
42}
43
44void ReadState::onEntry(QEvent *event)
45{
46 Q_UNUSED(event)
47 char *line = readline(prompt());
48
49 if (!line) {
50 std::cout << std::endl;
51 emit exitRequested();
52 return;
53 }
54
55 // we have actual data, so let's wait for a full line of text
56 QByteArray input(line);
57 const QString text = QString(line).simplified();
58 //qDebug() << "Line is ... " << text;
59
60 if (text.length() > 0) {
61 add_history(line);
62 }
63
64 free(line);
65 emit command(text);
66}
67
68const char *ReadState::prompt() const
69{
70 return "> ";
71}
72
73UnfinishedReadState::UnfinishedReadState(QState *parent)
74 : ReadState(parent)
75{
76}
77
78const char *UnfinishedReadState::prompt() const
79{
80 return " ";
81}
82
83EvalState::EvalState(QState *parent)
84 : QState(parent),
85 m_complete(false)
86{
87}
88
89void EvalState::onEntry(QEvent *event)
90{
91 QStateMachine::SignalEvent *e = dynamic_cast<QStateMachine::SignalEvent*>(event);
92
93 if (!e || e->arguments().isEmpty()) {
94 if (m_complete) {
95 emit completed();
96 } else {
97 emit continueInput();
98 }
99 return;
100 }
101
102 //TODO: properly tokenize (e.g. "foo bar" should not become ['"foo', 'bar"']
103 const QString command = e->arguments()[0].toString();
104
105 if (!command.isEmpty()) {
106 m_complete = command.right(1) != "\\";
107 if (m_complete) {
108 //emit output("Processing ... " + command);
109 const QStringList commands = command.split(" ");
110 Module::run(commands);
111 emit completed();
112 }
113 }
114}
115
116PrintState::PrintState(QState *parent)
117 : QState(parent)
118{
119}
120
121void PrintState::onEntry(QEvent *event)
122{
123 QStateMachine::SignalEvent *e = dynamic_cast<QStateMachine::SignalEvent*>(event);
124
125 if (e && !e->arguments().isEmpty()) {
126 const QString command = e->arguments()[0].toString();
127 QTextStream stream(stdout);
128 stream << command << "\n";
129 }
130
131 emit completed();
132}
133
134static QStringList tab_completion_full_state;
135static bool tab_completion_at_root = false;
136
137static char **akonadi2_cli_tab_completion(const char *text, int start, int end)
138{
139 tab_completion_at_root = start == 0;
140 tab_completion_full_state = QString(rl_line_buffer).remove(start, end - start).split(" ", QString::SkipEmptyParts);
141 return NULL;
142}
143
144static char *akonadi2_cli_next_tab_complete_match(const char *text, int state)
145{
146 QVector<Module::Syntax> nearest = Module::nearestSyntax(tab_completion_full_state, QString(text));
147
148 if (nearest.size() > state) {
149 return qstrdup(nearest[state].keyword.toUtf8());
150 }
151
152 return rl_filename_completion_function(text, state);
153}
154
155#include "moc_replStates.cpp"
diff --git a/akonadi2_cli/repl/replStates.h b/akonadi2_cli/repl/replStates.h
new file mode 100644
index 0000000..a019a37
--- /dev/null
+++ b/akonadi2_cli/repl/replStates.h
@@ -0,0 +1,85 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include <QState>
23
24class QSocketNotifier;
25
26class ReadState : public QState
27{
28 Q_OBJECT
29
30public:
31 ReadState(QState *parent = 0);
32
33Q_SIGNALS:
34 void command(const QString &command);
35 void exitRequested();
36
37protected:
38 void onEntry(QEvent *event);
39 virtual const char *prompt() const;
40};
41
42class UnfinishedReadState : public ReadState
43{
44 Q_OBJECT
45
46public:
47 UnfinishedReadState(QState *parent = 0);
48
49protected:
50 const char *prompt() const;
51};
52
53class EvalState : public QState
54{
55 Q_OBJECT
56
57public:
58 EvalState(QState *parent = 0);
59
60Q_SIGNALS:
61 void completed();
62 void continueInput();
63 void output(const QString &output);
64
65protected:
66 void onEntry(QEvent *event);
67
68private:
69 bool m_complete;
70};
71
72class PrintState : public QState
73{
74 Q_OBJECT
75
76public:
77 PrintState(QState *parent = 0);
78
79Q_SIGNALS:
80 void completed();
81
82protected:
83 void onEntry(QEvent *event);
84};
85
diff --git a/akonadi2_cli/state.cpp b/akonadi2_cli/state.cpp
new file mode 100644
index 0000000..bb21e0e
--- /dev/null
+++ b/akonadi2_cli/state.cpp
@@ -0,0 +1,28 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "state.h"
21
22#include <QDebug>
23#include <QDir>
24
25State::State()
26{
27}
28
diff --git a/akonadi2_cli/state.h b/akonadi2_cli/state.h
new file mode 100644
index 0000000..10b2318
--- /dev/null
+++ b/akonadi2_cli/state.h
@@ -0,0 +1,29 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include <QStringList>
23
24class State
25{
26public:
27 State();
28};
29
diff --git a/cmake/modules/FindReadline.cmake b/cmake/modules/FindReadline.cmake
new file mode 100644
index 0000000..883ad3f
--- /dev/null
+++ b/cmake/modules/FindReadline.cmake
@@ -0,0 +1,47 @@
1# - Try to find readline include dirs and libraries
2#
3# Usage of this module as follows:
4#
5# find_package(Readline)
6#
7# Variables used by this module, they can change the default behaviour and need
8# to be set before calling find_package:
9#
10# Readline_ROOT_DIR Set this variable to the root installation of
11# readline if the module has problems finding the
12# proper installation path.
13#
14# Variables defined by this module:
15#
16# READLINE_FOUND System has readline, include and lib dirs found
17# Readline_INCLUDE_DIR The readline include directories.
18# Readline_LIBRARY The readline library.
19
20find_path(Readline_ROOT_DIR
21 NAMES include/readline/readline.h
22 )
23
24find_path(Readline_INCLUDE_DIR
25 NAMES readline/readline.h
26 HINTS ${Readline_ROOT_DIR}/include
27 )
28
29find_library(Readline_LIBRARY
30 NAMES readline
31 HINTS ${Readline_ROOT_DIR}/lib
32 )
33
34if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
35 set(READLINE_FOUND TRUE)
36else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
37 FIND_LIBRARY(Readline_LIBRARY NAMES readline)
38 include(FindPackageHandleStandardArgs)
39 FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY )
40 MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY)
41endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
42
43mark_as_advanced(
44 Readline_ROOT_DIR
45 Readline_INCLUDE_DIR
46 Readline_LIBRARY
47 )