summaryrefslogtreecommitdiffstats
path: root/sinksh
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2016-01-20 19:07:07 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2016-01-20 19:07:07 +0100
commitbdb01c2c068df326f5a8328ed1492ab1bea388c5 (patch)
tree25c2ee1b29bc481b6914c244ed9ca194b1415d16 /sinksh
parent17e7ee40c9185c0505883853345fd6024c675b1a (diff)
downloadsink-bdb01c2c068df326f5a8328ed1492ab1bea388c5.tar.gz
sink-bdb01c2c068df326f5a8328ed1492ab1bea388c5.zip
Renamed Akonadi2 to Sink
(except for documentation).
Diffstat (limited to 'sinksh')
-rw-r--r--sinksh/CMakeLists.txt29
-rw-r--r--sinksh/TODO11
-rw-r--r--sinksh/main.cpp115
-rw-r--r--sinksh/repl/repl.cpp91
-rw-r--r--sinksh/repl/repl.h35
-rw-r--r--sinksh/repl/replStates.cpp171
-rw-r--r--sinksh/repl/replStates.h87
-rw-r--r--sinksh/sinksh_utils.cpp134
-rw-r--r--sinksh/sinksh_utils.h88
-rw-r--r--sinksh/state.cpp144
-rw-r--r--sinksh/state.h52
-rw-r--r--sinksh/syntax_modules/core_syntax.cpp204
-rw-r--r--sinksh/syntax_modules/sink_clear.cpp61
-rw-r--r--sinksh/syntax_modules/sink_count.cpp83
-rw-r--r--sinksh/syntax_modules/sink_create.cpp118
-rw-r--r--sinksh/syntax_modules/sink_list.cpp114
-rw-r--r--sinksh/syntax_modules/sink_modify.cpp120
-rw-r--r--sinksh/syntax_modules/sink_remove.cpp110
-rw-r--r--sinksh/syntax_modules/sink_stat.cpp120
-rw-r--r--sinksh/syntax_modules/sink_sync.cpp67
-rw-r--r--sinksh/syntaxtree.cpp221
-rw-r--r--sinksh/syntaxtree.h80
-rw-r--r--sinksh/utils.cpp42
-rw-r--r--sinksh/utils.h30
24 files changed, 2327 insertions, 0 deletions
diff --git a/sinksh/CMakeLists.txt b/sinksh/CMakeLists.txt
new file mode 100644
index 0000000..1489fb3
--- /dev/null
+++ b/sinksh/CMakeLists.txt
@@ -0,0 +1,29 @@
1project(sinksh)
2
3find_package(Readline REQUIRED)
4
5
6set(sink_cli_SRCS
7 main.cpp
8 syntaxtree.cpp
9 syntax_modules/core_syntax.cpp
10 syntax_modules/sink_list.cpp
11 syntax_modules/sink_clear.cpp
12 syntax_modules/sink_count.cpp
13 syntax_modules/sink_create.cpp
14 syntax_modules/sink_modify.cpp
15 syntax_modules/sink_remove.cpp
16 syntax_modules/sink_stat.cpp
17 syntax_modules/sink_sync.cpp
18 sinksh_utils.cpp
19 repl/repl.cpp
20 repl/replStates.cpp
21 state.cpp
22 utils.cpp)
23
24include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
25
26add_executable(${PROJECT_NAME} ${sink_cli_SRCS})
27target_link_libraries(${PROJECT_NAME} Qt5::Core ${Readline_LIBRARY} sinkcommon)
28install(TARGETS ${PROJECT_NAME} ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
29
diff --git a/sinksh/TODO b/sinksh/TODO
new file mode 100644
index 0000000..93c4ff1
--- /dev/null
+++ b/sinksh/TODO
@@ -0,0 +1,11 @@
1* commands
2 * improve modify/remove/create to dynamically add syntax items for each type
3 * improve modify/remove/create to autocomplete resource names
4* provide a setting to turn on/off user interaction during commands (e.g. deletion confirmations)
5 * add a "ask the user a question" helper in State
6 * confirm deletions in remove
7* handle env vars and similar for scripting purposes
8* key/value syntax objects
9* json objects! (set json on; ...)
10* make the shell generic and have it load a plugin matching the name of argv[0] for syntax
11
diff --git a/sinksh/main.cpp b/sinksh/main.cpp
new file mode 100644
index 0000000..4c00b9b
--- /dev/null
+++ b/sinksh/main.cpp
@@ -0,0 +1,115 @@
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#include <QFile>
25#include <QTextStream>
26
27#include "syntaxtree.h"
28// #include "jsonlistener.h"
29#include "repl/repl.h"
30
31/*
32 * modes of operation:
33 *
34 * 1. called with no commands: start the REPL
35 * 2. called with -: listen for commands on stdin
36 * 3. called with a filename: try to run it as a script
37 * 4. called with commands: try to match to syntax and run the result
38 */
39
40int enterRepl()
41{
42 if (State::hasEventLoop()) {
43 return 0;
44 }
45
46 Repl *repl = new Repl;
47 QObject::connect(repl, &QStateMachine::finished,
48 repl, &QObject::deleteLater);
49 QObject::connect(repl, &QStateMachine::finished,
50 QCoreApplication::instance(), &QCoreApplication::quit);
51
52 State::setHasEventLoop(true);
53 int rv = QCoreApplication::instance()->exec();
54 State::setHasEventLoop(false);
55 return rv;
56}
57
58bool goInteractive(const QStringList &, State &)
59{
60 enterRepl();
61 return true;
62}
63
64Syntax::List goInteractiveSyntax()
65{
66 Syntax interactive("go_interactive", QString(), &goInteractive);
67 return Syntax::List() << interactive;
68}
69
70void processCommandStream(QTextStream &stream)
71{
72 SyntaxTree::self()->registerSyntax(&goInteractiveSyntax);
73 QString line = stream.readLine();
74 while (!line.isEmpty()) {
75 line = line.trimmed();
76
77 if (!line.isEmpty() && !line.startsWith('#')) {
78 SyntaxTree::self()->run(SyntaxTree::tokenize(line));
79 }
80
81 line = stream.readLine();
82 }
83}
84
85int main(int argc, char *argv[])
86{
87 const bool interactive = isatty(fileno(stdin));
88 const bool startRepl = (argc == 1) && interactive;
89 //TODO: make a json command parse cause that would be awesomesauce
90 const bool fromScript = !startRepl && QFile::exists(argv[1]);
91
92 //qDebug() << "state at startup is" << interactive << startRepl << fromScript;
93
94 QCoreApplication app(argc, argv);
95 app.setApplicationName(fromScript ? "interactive-app-shell" : argv[0]);
96
97 if (startRepl) {
98 return enterRepl();
99 } else if (fromScript) {
100 QFile f(argv[1]);
101 if (!f.open(QIODevice::ReadOnly)) {
102 return 1;
103 }
104
105 QTextStream inputStream(&f);
106 processCommandStream(inputStream);
107 } else if (!interactive) {
108 QTextStream inputStream(stdin);
109 processCommandStream(inputStream);
110 } else {
111 QStringList commands = app.arguments();
112 commands.removeFirst();
113 return SyntaxTree::self()->run(commands);
114 }
115}
diff --git a/sinksh/repl/repl.cpp b/sinksh/repl/repl.cpp
new file mode 100644
index 0000000..21209fc
--- /dev/null
+++ b/sinksh/repl/repl.cpp
@@ -0,0 +1,91 @@
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#include <QTextStream>
29
30#include "replStates.h"
31#include "syntaxtree.h"
32
33Repl::Repl(QObject *parent)
34 : QStateMachine(parent)
35{
36 // readline history setup
37 using_history();
38 read_history(commandHistoryPath().toLocal8Bit());
39
40 // create all states
41 ReadState *read = new ReadState(this);
42 UnfinishedReadState *unfinishedRead = new UnfinishedReadState(this);
43 EvalState *eval = new EvalState(this);
44 PrintState *print = new PrintState(this);
45 QFinalState *final = new QFinalState(this);
46
47 // connect the transitions
48 read->addTransition(read, SIGNAL(command(QString)), eval);
49 read->addTransition(read, SIGNAL(exitRequested()), final);
50
51 unfinishedRead->addTransition(unfinishedRead, SIGNAL(command(QString)), eval);
52 unfinishedRead->addTransition(unfinishedRead, SIGNAL(exitRequested()), final);
53
54 eval->addTransition(eval, SIGNAL(completed()), read);
55 eval->addTransition(eval, SIGNAL(continueInput()), unfinishedRead);
56 eval->addTransition(eval, SIGNAL(output(QString)), print);
57
58 print->addTransition(print, SIGNAL(completed()), eval);
59
60 setInitialState(read);
61 printWelcomeBanner();
62 start();
63}
64
65Repl::~Repl()
66{
67 // readline history writing
68 write_history(commandHistoryPath().toLocal8Bit());
69}
70
71void Repl::printWelcomeBanner()
72{
73 QTextStream out(stdout);
74 out << QObject::tr("Welcome to the Sink interative shell!\n");
75 out << QObject::tr("Type `help` for information on the available commands.\n");
76 out.flush();
77}
78
79QString Repl::commandHistoryPath()
80{
81 const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
82
83 if (!QFile::exists(path)) {
84 QDir dir;
85 dir.mkpath(path);
86 }
87
88 return path + "/repl_history";
89}
90
91#include "moc_repl.cpp"
diff --git a/sinksh/repl/repl.h b/sinksh/repl/repl.h
new file mode 100644
index 0000000..d8d2533
--- /dev/null
+++ b/sinksh/repl/repl.h
@@ -0,0 +1,35 @@
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 void printWelcomeBanner();
34 static QString commandHistoryPath();
35};
diff --git a/sinksh/repl/replStates.cpp b/sinksh/repl/replStates.cpp
new file mode 100644
index 0000000..43b1353
--- /dev/null
+++ b/sinksh/repl/replStates.cpp
@@ -0,0 +1,171 @@
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 "syntaxtree.h"
33
34static char *sink_cli_next_tab_complete_match(const char *text, int state);
35static char ** sink_cli_tab_completion(const char *text, int start, int end);
36
37ReadState::ReadState(QState *parent)
38 : QState(parent)
39{
40 rl_completion_entry_function = sink_cli_next_tab_complete_match;
41 rl_attempted_completion_function = sink_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{
86}
87
88void EvalState::onEntry(QEvent *event)
89{
90 QStateMachine::SignalEvent *e = dynamic_cast<QStateMachine::SignalEvent*>(event);
91
92 const QString command = e ? e->arguments()[0].toString() : QString();
93
94 if (command.isEmpty()) {
95 complete();
96 return;
97 }
98
99 if (command.right(1) == "\\") {
100 m_partial += " " + command.left(command.size() - 1);
101 continueInput();
102 } else {
103 m_partial += " " + command;
104 complete();
105 }
106}
107
108void EvalState::complete()
109{
110 m_partial = m_partial.simplified();
111
112 if (!m_partial.isEmpty()) {
113 //emit output("Processing ... " + command);
114 const QStringList commands = SyntaxTree::tokenize(m_partial);
115 SyntaxTree::self()->run(commands);
116 m_partial.clear();
117 }
118
119 emit completed();
120}
121
122PrintState::PrintState(QState *parent)
123 : QState(parent)
124{
125}
126
127void PrintState::onEntry(QEvent *event)
128{
129 QStateMachine::SignalEvent *e = dynamic_cast<QStateMachine::SignalEvent*>(event);
130
131 if (e && !e->arguments().isEmpty()) {
132 const QString command = e->arguments()[0].toString();
133 QTextStream stream(stdout);
134 stream << command << "\n";
135 }
136
137 emit completed();
138}
139
140static QStringList tab_completion_full_state;
141static bool tab_completion_at_root = false;
142
143static char **sink_cli_tab_completion(const char *text, int start, int end)
144{
145 tab_completion_at_root = start == 0;
146 tab_completion_full_state = QString(rl_line_buffer).remove(start, end - start).split(" ", QString::SkipEmptyParts);
147 return NULL;
148}
149
150static char *sink_cli_next_tab_complete_match(const char *text, int state)
151{
152 const QString fragment(text);
153 Syntax::List nearest = SyntaxTree::self()->nearestSyntax(tab_completion_full_state, fragment);
154 //for (auto syntax: nearest) { qDebug() << "Nearest: " << syntax.keyword; }
155
156 if (nearest.isEmpty()) {
157 SyntaxTree::Command command = SyntaxTree::self()->match(tab_completion_full_state);
158 if (command.first && command.first->completer) {
159 QStringList commandCompletions = command.first->completer(tab_completion_full_state, fragment, SyntaxTree::self()->state());
160 if (commandCompletions.size() > state) {
161 return qstrdup(commandCompletions[state].toUtf8());
162 }
163 }
164 } else if (nearest.size() > state) {
165 return qstrdup(nearest[state].keyword.toUtf8());
166 }
167
168 return rl_filename_completion_function(text, state);
169}
170
171#include "moc_replStates.cpp"
diff --git a/sinksh/repl/replStates.h b/sinksh/repl/replStates.h
new file mode 100644
index 0000000..a0d3f90
--- /dev/null
+++ b/sinksh/repl/replStates.h
@@ -0,0 +1,87 @@
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 void complete();
70
71 QString m_partial;
72};
73
74class PrintState : public QState
75{
76 Q_OBJECT
77
78public:
79 PrintState(QState *parent = 0);
80
81Q_SIGNALS:
82 void completed();
83
84protected:
85 void onEntry(QEvent *event);
86};
87
diff --git a/sinksh/sinksh_utils.cpp b/sinksh/sinksh_utils.cpp
new file mode 100644
index 0000000..fa06b34
--- /dev/null
+++ b/sinksh/sinksh_utils.cpp
@@ -0,0 +1,134 @@
1/*
2 * Copyright (C) 2015 Aaron Seigo <aseigo@kolabsystems.com>
3 * Copyright (C) 2015 Christian Mollekopf <mollekopf@kolabsystems.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "sinksh_utils.h"
22
23#include "common/clientapi.h"
24
25#include "utils.h"
26
27namespace SinkshUtils
28{
29
30static QStringList s_types = QStringList() << "resource" << "folder" << "mail" << "event";
31
32bool isValidStoreType(const QString &type)
33{
34 return s_types.contains(type);
35}
36
37StoreBase &getStore(const QString &type)
38{
39 if (type == "folder") {
40 static Store<Sink::ApplicationDomain::Folder> store;
41 return store;
42 } else if (type == "mail") {
43 static Store<Sink::ApplicationDomain::Mail> store;
44 return store;
45 } else if (type == "event") {
46 static Store<Sink::ApplicationDomain::Event> store;
47 return store;
48 } else if (type == "resource") {
49 static Store<Sink::ApplicationDomain::SinkResource> store;
50 return store;
51 }
52
53 //TODO: reinstate the warning+assert
54 //Q_ASSERT(false);
55 //qWarning() << "Trying to get a store that doesn't exist, falling back to event";
56 static Store<Sink::ApplicationDomain::Event> store;
57 return store;
58}
59
60QSharedPointer<QAbstractItemModel> loadModel(const QString &type, Sink::Query query)
61{
62 if (type == "folder") {
63 query.requestedProperties << "name" << "parent";
64 } else if (type == "mail") {
65 query.requestedProperties << "subject" << "folder" << "date";
66 } else if (type == "event") {
67 query.requestedProperties << "summary";
68 } else if (type == "resource") {
69 query.requestedProperties << "type";
70 }
71 auto model = getStore(type).loadModel(query);
72 Q_ASSERT(model);
73 return model;
74}
75
76QStringList resourceIds(State &state)
77{
78 QStringList resources;
79 Sink::Query query;
80 query.liveQuery = false;
81 auto model = SinkshUtils::loadModel("resource", query);
82
83 QObject::connect(model.data(), &QAbstractItemModel::rowsInserted, [model, &resources] (const QModelIndex &index, int start, int end) mutable {
84 for (int i = start; i <= end; i++) {
85 auto object = model->data(model->index(i, 0, index), Sink::Store::DomainObjectBaseRole).value<Sink::ApplicationDomain::ApplicationDomainType::Ptr>();
86 resources << object->identifier();
87 }
88 });
89
90 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [model, state](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
91 if (roles.contains(Sink::Store::ChildrenFetchedRole)) {
92 state.commandFinished();
93 }
94 });
95
96 state.commandStarted();
97
98 return resources;
99}
100
101QStringList resourceCompleter(const QStringList &, const QString &fragment, State &state)
102{
103 return Utils::filteredCompletions(resourceIds(state), fragment);
104}
105
106QStringList resourceOrTypeCompleter(const QStringList &commands, const QString &fragment, State &state)
107{
108 static QStringList types = QStringList() << "resource" << "folder" << "mail" << "event";
109 if (commands.count() == 1) {
110 return Utils::filteredCompletions(s_types, fragment);
111 }
112
113 return Utils::filteredCompletions(resourceIds(state), fragment);
114}
115
116QStringList typeCompleter(const QStringList &commands, const QString &fragment, State &state)
117{
118 return Utils::filteredCompletions(s_types, fragment);
119}
120
121QMap<QString, QString> keyValueMapFromArgs(const QStringList &args)
122{
123 //TODO: this is not the most clever of algorithms. preserved during the port of commands
124 // from sink_client ... we can probably do better, however ;)
125 QMap<QString, QString> map;
126 for (int i = 0; i + 2 <= args.size(); i += 2) {
127 map.insert(args.at(i), args.at(i + 1));
128 }
129
130 return map;
131}
132
133}
134
diff --git a/sinksh/sinksh_utils.h b/sinksh/sinksh_utils.h
new file mode 100644
index 0000000..457f644
--- /dev/null
+++ b/sinksh/sinksh_utils.h
@@ -0,0 +1,88 @@
1/*
2 * Copyright (C) 2015 Aaron Seigo <aseigo@kolabsystems.com>
3 * Copyright (C) 2015 Christian Mollekopf <mollekopf@kolabsystems.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#pragma once
22
23#include <QAbstractItemModel>
24#include <QSharedPointer>
25
26#include "common/query.h"
27#include "common/clientapi.h"
28
29#include "state.h"
30
31namespace SinkshUtils
32{
33
34class StoreBase;
35
36bool isValidStoreType(const QString &type);
37StoreBase &getStore(const QString &type);
38QSharedPointer<QAbstractItemModel> loadModel(const QString &type, Sink::Query query);
39QStringList resourceIds();
40QStringList resourceCompleter(const QStringList &, const QString &fragment, State &state);
41QStringList resourceOrTypeCompleter(const QStringList &commands, const QString &fragment, State &state);
42QStringList typeCompleter(const QStringList &commands, const QString &fragment, State &state);
43QMap<QString, QString> keyValueMapFromArgs(const QStringList &args);
44
45/**
46 * A small abstraction layer to use the sink store with the type available as string.
47 */
48class StoreBase {
49public:
50 virtual Sink::ApplicationDomain::ApplicationDomainType::Ptr getObject() = 0;
51 virtual Sink::ApplicationDomain::ApplicationDomainType::Ptr getObject(const QByteArray &resourceInstanceIdentifier, const QByteArray &identifier = QByteArray()) = 0;
52 virtual KAsync::Job<void> create(const Sink::ApplicationDomain::ApplicationDomainType &type) = 0;
53 virtual KAsync::Job<void> modify(const Sink::ApplicationDomain::ApplicationDomainType &type) = 0;
54 virtual KAsync::Job<void> remove(const Sink::ApplicationDomain::ApplicationDomainType &type) = 0;
55 virtual QSharedPointer<QAbstractItemModel> loadModel(const Sink::Query &query) = 0;
56};
57
58template <typename T>
59class Store : public StoreBase {
60public:
61 Sink::ApplicationDomain::ApplicationDomainType::Ptr getObject() Q_DECL_OVERRIDE {
62 return T::Ptr::create();
63 }
64
65 Sink::ApplicationDomain::ApplicationDomainType::Ptr getObject(const QByteArray &resourceInstanceIdentifier, const QByteArray &identifier = QByteArray()) Q_DECL_OVERRIDE {
66 return T::Ptr::create(resourceInstanceIdentifier, identifier, 0, QSharedPointer<Sink::ApplicationDomain::MemoryBufferAdaptor>::create());
67 }
68
69 KAsync::Job<void> create(const Sink::ApplicationDomain::ApplicationDomainType &type) Q_DECL_OVERRIDE {
70 return Sink::Store::create<T>(*static_cast<const T*>(&type));
71 }
72
73 KAsync::Job<void> modify(const Sink::ApplicationDomain::ApplicationDomainType &type) Q_DECL_OVERRIDE {
74 return Sink::Store::modify<T>(*static_cast<const T*>(&type));
75 }
76
77 KAsync::Job<void> remove(const Sink::ApplicationDomain::ApplicationDomainType &type) Q_DECL_OVERRIDE {
78 return Sink::Store::remove<T>(*static_cast<const T*>(&type));
79 }
80
81 QSharedPointer<QAbstractItemModel> loadModel(const Sink::Query &query) Q_DECL_OVERRIDE {
82 return Sink::Store::loadModel<T>(query);
83 }
84};
85
86
87}
88
diff --git a/sinksh/state.cpp b/sinksh/state.cpp
new file mode 100644
index 0000000..e03bf87
--- /dev/null
+++ b/sinksh/state.cpp
@@ -0,0 +1,144 @@
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 <QCoreApplication>
23#include <QDebug>
24#include <QEventLoop>
25#include <QTextStream>
26
27#include "common/log.h"
28
29static bool s_hasEventLoop = false;
30
31class State::Private
32{
33public:
34 Private()
35 : outStream(stdout)
36 {
37 }
38
39 QEventLoop *eventLoop()
40 {
41 if (!event) {
42 event = new QEventLoop;
43 }
44
45 return event;
46 }
47
48 int debugLevel = 0;
49 QEventLoop *event = 0;
50 bool timing = false;
51 QTextStream outStream;
52};
53
54State::State()
55 : d(new Private)
56{
57}
58
59void State::print(const QString &message, unsigned int indentationLevel) const
60{
61 for (unsigned int i = 0; i < indentationLevel; ++i) {
62 d->outStream << "\t";
63 }
64
65 d->outStream << message;
66}
67
68void State::printLine(const QString &message, unsigned int indentationLevel) const
69{
70 print(message, indentationLevel);
71 d->outStream << "\n";
72 d->outStream.flush();
73}
74
75void State::printError(const QString &errorMessage, const QString &errorCode) const
76{
77 printLine("ERROR" + (errorCode.isEmpty() ? "" : " " + errorCode) + ": " + errorMessage);
78}
79
80void State::setDebugLevel(unsigned int level)
81{
82 if (level < 7) {
83 d->debugLevel = level;
84 }
85}
86
87unsigned int State::debugLevel() const
88{
89 return d->debugLevel;
90}
91
92int State::commandStarted() const
93{
94 if (!s_hasEventLoop) {
95 return QCoreApplication::exec();
96 } else if (!d->eventLoop()->isRunning()) {
97 return d->eventLoop()->exec();
98 }
99
100 return 0;
101}
102
103void State::commandFinished(int returnCode) const
104{
105 if (!s_hasEventLoop) {
106 QCoreApplication::exit(returnCode);
107 } else {
108 d->eventLoop()->exit(returnCode);
109 }
110}
111
112void State::setHasEventLoop(bool evented)
113{
114 s_hasEventLoop = evented;
115}
116
117bool State::hasEventLoop()
118{
119 return s_hasEventLoop;
120}
121
122void State::setCommandTiming(bool time)
123{
124 d->timing = time;
125}
126
127bool State::commandTiming() const
128{
129 return d->timing;
130}
131
132void State::setLoggingLevel(const QString &level) const
133{
134 Sink::Log::setDebugOutputLevel(Sink::Log::debugLevelFromName(level.toLatin1()));
135}
136
137QString State::loggingLevel() const
138{
139 // do not turn this into a single line return: that core dumps due to allocation of
140 // the byte array in Sink::Log
141 QByteArray rv = Sink::Log::debugLevelName(Sink::Log::debugOutputLevel());
142 return rv.toLower();
143}
144
diff --git a/sinksh/state.h b/sinksh/state.h
new file mode 100644
index 0000000..3c4c2c7
--- /dev/null
+++ b/sinksh/state.h
@@ -0,0 +1,52 @@
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 <QTextStream>
23
24class State
25{
26public:
27 State();
28
29 void print(const QString &message, unsigned int indentationLevel = 0) const;
30 void printLine(const QString &message = QString(), unsigned int indentationLevel = 0) const;
31 void printError(const QString &errorMessage, const QString &errorCode = QString()) const;
32
33 void setDebugLevel(unsigned int level);
34 unsigned int debugLevel() const;
35
36 void setCommandTiming(bool);
37 bool commandTiming() const;
38
39 int commandStarted() const;
40 void commandFinished(int returnCode = 0) const;
41
42 void setLoggingLevel(const QString &level) const;
43 QString loggingLevel() const;
44
45 static void setHasEventLoop(bool evented);
46 static bool hasEventLoop();
47
48private:
49 class Private;
50 Private * const d;
51};
52
diff --git a/sinksh/syntax_modules/core_syntax.cpp b/sinksh/syntax_modules/core_syntax.cpp
new file mode 100644
index 0000000..f5b6274
--- /dev/null
+++ b/sinksh/syntax_modules/core_syntax.cpp
@@ -0,0 +1,204 @@
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 <QDebug>
21#include <QObject> // tr()
22#include <QSet>
23#include <QTextStream>
24
25#include "state.h"
26#include "syntaxtree.h"
27#include "utils.h"
28
29namespace CoreSyntax
30{
31
32bool exit(const QStringList &, State &)
33{
34 ::exit(0);
35 return true;
36}
37
38bool showHelp(const QStringList &commands, State &state)
39{
40 SyntaxTree::Command command = SyntaxTree::self()->match(commands);
41 if (commands.isEmpty()) {
42 state.printLine(QObject::tr("Welcome to the Sink command line tool!"));
43 state.printLine(QObject::tr("Top-level commands:"));
44
45 QSet<QString> sorted;
46 for (auto syntax: SyntaxTree::self()->syntax()) {
47 sorted.insert(syntax.keyword);
48 }
49
50 for (auto keyword: sorted) {
51 state.printLine(keyword, 1);
52 }
53 } else if (const Syntax *syntax = command.first) {
54 //TODO: get parent!
55 state.print(QObject::tr("Command `%1`").arg(syntax->keyword));
56
57 if (!syntax->help.isEmpty()) {
58 state.print(": " + syntax->help);
59 }
60 state.printLine();
61
62 if (!syntax->children.isEmpty()) {
63 state.printLine("Sub-commands:", 1);
64 QSet<QString> sorted;
65 for (auto childSyntax: syntax->children) {
66 sorted.insert(childSyntax.keyword);
67 }
68
69 for (auto keyword: sorted) {
70 state.printLine(keyword, 1);
71 }
72 }
73 } else {
74 state.printError("Unknown command: " + commands.join(" "));
75 }
76
77 return true;
78}
79
80QStringList showHelpCompleter(const QStringList &commands, const QString &fragment, State &)
81{
82 QStringList items;
83
84 for (auto syntax: SyntaxTree::self()->syntax()) {
85 if (syntax.keyword != QObject::tr("help") &&
86 (fragment.isEmpty() || syntax.keyword.startsWith(fragment))) {
87 items << syntax.keyword;
88 }
89 }
90
91 qSort(items);
92 return items;
93}
94
95bool setDebugLevel(const QStringList &commands, State &state)
96{
97 if (commands.count() != 1) {
98 state.printError(QObject::tr("Wrong number of arguments; expected 1 got %1").arg(commands.count()));
99 return false;
100 }
101
102 bool ok = false;
103 int level = commands[0].toUInt(&ok);
104
105 if (!ok) {
106 state.printError(QObject::tr("Expected a number between 0 and 6, got %1").arg(commands[0]));
107 return false;
108 }
109
110 state.setDebugLevel(level);
111 return true;
112}
113
114bool printDebugLevel(const QStringList &, State &state)
115{
116 state.printLine(QString::number(state.debugLevel()));
117 return true;
118}
119
120bool printCommandTiming(const QStringList &, State &state)
121{
122 state.printLine(state.commandTiming() ? QObject::tr("on") : QObject::tr("off"));
123 return true;
124}
125
126void printSyntaxBranch(State &state, const Syntax::List &list, int depth)
127{
128 if (list.isEmpty()) {
129 return;
130 }
131
132 if (depth > 0) {
133 state.printLine("\\", depth);
134 }
135
136 for (auto syntax: list) {
137 state.print("|-", depth);
138 state.printLine(syntax.keyword);
139 printSyntaxBranch(state, syntax.children, depth + 1);
140 }
141}
142
143bool printSyntaxTree(const QStringList &, State &state)
144{
145 printSyntaxBranch(state, SyntaxTree::self()->syntax(), 0);
146 return true;
147}
148
149bool setLoggingLevel(const QStringList &commands, State &state)
150{
151 if (commands.count() != 1) {
152 state.printError(QObject::tr("Wrong number of arguments; expected 1 got %1").arg(commands.count()));
153 return false;
154 }
155
156 state.setLoggingLevel(commands.at(0));
157 return true;
158}
159
160bool printLoggingLevel(const QStringList &commands, State &state)
161{
162 const QString level = state.loggingLevel();
163 state.printLine(level);
164 return true;
165}
166
167Syntax::List syntax()
168{
169 Syntax::List syntax;
170 syntax << Syntax("exit", QObject::tr("Exits the application. Ctrl-d also works!"), &CoreSyntax::exit);
171
172 Syntax help("help", QObject::tr("Print command information: help [command]"), &CoreSyntax::showHelp);
173 help.completer = &CoreSyntax::showHelpCompleter;
174 syntax << help;
175
176 syntax << Syntax("syntaxtree", QString(), &printSyntaxTree);
177
178 Syntax set("set", QObject::tr("Sets settings for the session"));
179 set.children << Syntax("debug", QObject::tr("Set the debug level from 0 to 6"), &CoreSyntax::setDebugLevel);
180
181 Syntax setTiming = Syntax("timing", QObject::tr("Whether or not to print the time commands take to complete"));
182 setTiming.children << Syntax("on", QString(), [](const QStringList &, State &state) -> bool { state.setCommandTiming(true); return true; });
183 setTiming.children << Syntax("off", QString(), [](const QStringList &, State &state) -> bool { state.setCommandTiming(false); return true; });
184 set.children << setTiming;
185
186 Syntax logging("logging", QObject::tr("Set the logging level to one of Trace, Log, Warning or Error"), &CoreSyntax::setLoggingLevel);
187 logging.completer = [](const QStringList &, const QString &fragment, State &state) -> QStringList { return Utils::filteredCompletions(QStringList() << "trace" << "log" << "warning" << "error", fragment, Qt::CaseInsensitive); };
188 set.children << logging;
189
190 syntax << set;
191
192 Syntax get("get", QObject::tr("Gets settings for the session"));
193 get.children << Syntax("debug", QObject::tr("The current debug level from 0 to 6"), &CoreSyntax::printDebugLevel);
194 get.children << Syntax("timing", QObject::tr("Whether or not to print the time commands take to complete"), &CoreSyntax::printCommandTiming);
195 get.children << Syntax("logging", QObject::tr("The current logging level"), &CoreSyntax::printLoggingLevel);
196 syntax << get;
197
198 return syntax;
199}
200
201REGISTER_SYNTAX(CoreSyntax)
202
203} // namespace CoreSyntax
204
diff --git a/sinksh/syntax_modules/sink_clear.cpp b/sinksh/syntax_modules/sink_clear.cpp
new file mode 100644
index 0000000..d02c638
--- /dev/null
+++ b/sinksh/syntax_modules/sink_clear.cpp
@@ -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#include <QDebug>
21#include <QObject> // tr()
22#include <QTimer>
23
24#include "common/resource.h"
25#include "common/storage.h"
26#include "common/domain/event.h"
27#include "common/domain/folder.h"
28#include "common/resourceconfig.h"
29#include "common/log.h"
30#include "common/storage.h"
31#include "common/definitions.h"
32
33#include "sinksh_utils.h"
34#include "state.h"
35#include "syntaxtree.h"
36
37namespace SinkClear
38{
39
40bool clear(const QStringList &args, State &state)
41{
42 for (const auto &resource : args) {
43 state.print(QObject::tr("Removing local cache for '%1' ...").arg(resource));
44 Sink::Store::removeFromDisk(resource.toLatin1());
45 state.printLine(QObject::tr("done"));
46 }
47
48 return true;
49}
50
51Syntax::List syntax()
52{
53 Syntax clear("clear", QObject::tr("Clears the local cache of one or more resources (be careful!)"), &SinkClear::clear);
54 clear.completer = &SinkshUtils::resourceCompleter;
55
56 return Syntax::List() << clear;
57}
58
59REGISTER_SYNTAX(SinkClear)
60
61}
diff --git a/sinksh/syntax_modules/sink_count.cpp b/sinksh/syntax_modules/sink_count.cpp
new file mode 100644
index 0000000..fde7c33
--- /dev/null
+++ b/sinksh/syntax_modules/sink_count.cpp
@@ -0,0 +1,83 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "sinksh_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace SinkCount
40{
41
42bool count(const QStringList &args, State &state)
43{
44 auto resources = args;
45 auto type = !resources.isEmpty() ? resources.takeFirst() : QString();
46
47 if (!type.isEmpty() && !SinkshUtils::isValidStoreType(type)) {
48 state.printError(QObject::tr("Unknown type: %1").arg(type));
49 return false;
50 }
51
52 Sink::Query query;
53 for (const auto &res : resources) {
54 query.resources << res.toLatin1();
55 }
56 query.liveQuery = false;
57
58 auto model = SinkshUtils::loadModel(type, query);
59 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [model, state](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
60 if (roles.contains(Sink::Store::ChildrenFetchedRole)) {
61 state.printLine(QObject::tr("Counted results %1").arg(model->rowCount(QModelIndex())));
62 state.commandFinished();
63 }
64 });
65
66 if (!model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()) {
67 return true;
68 }
69
70 return true;
71}
72
73Syntax::List syntax()
74{
75 Syntax count("count", QObject::tr("Returns the number of items of a given type in a resource. Usage: count <type> <resource>"), &SinkCount::count, Syntax::EventDriven);
76 count.completer = &SinkshUtils::typeCompleter;
77
78 return Syntax::List() << count;
79}
80
81REGISTER_SYNTAX(SinkCount)
82
83}
diff --git a/sinksh/syntax_modules/sink_create.cpp b/sinksh/syntax_modules/sink_create.cpp
new file mode 100644
index 0000000..cd2cd80
--- /dev/null
+++ b/sinksh/syntax_modules/sink_create.cpp
@@ -0,0 +1,118 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "sinksh_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace SinkCreate
40{
41
42bool create(const QStringList &allArgs, State &state)
43{
44 if (allArgs.isEmpty()) {
45 state.printError(QObject::tr("A type is required"), "sinkcreate/02");
46 return false;
47 }
48
49 if (allArgs.count() < 2) {
50 state.printError(QObject::tr("A resource ID is required to create items"), "sinkcreate/03");
51 return false;
52 }
53
54 auto args = allArgs;
55 auto type = args.takeFirst();
56 auto &store = SinkshUtils::getStore(type);
57 Sink::ApplicationDomain::ApplicationDomainType::Ptr object;
58 auto resource = args.takeFirst().toLatin1();
59 object = store.getObject(resource);
60
61 auto map = SinkshUtils::keyValueMapFromArgs(args);
62 for (auto i = map.begin(); i != map.end(); ++i) {
63 object->setProperty(i.key().toLatin1(), i.value());
64 }
65
66 auto result = store.create(*object).exec();
67 result.waitForFinished();
68 if (result.errorCode()) {
69 state.printError(QObject::tr("An error occurred while creating the entity: %1").arg(result.errorMessage()),
70 "akonaid_create_e" + QString::number(result.errorCode()));
71 }
72
73 return true;
74}
75
76bool resource(const QStringList &args, State &state)
77{
78 if (args.isEmpty()) {
79 state.printError(QObject::tr("A resource can not be created without a type"), "sinkcreate/01");
80 return false;
81 }
82
83 auto &store = SinkshUtils::getStore("resource");
84
85 auto resourceType = args.at(0);
86 Sink::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject("");
87 object->setProperty("type", resourceType);
88
89 auto map = SinkshUtils::keyValueMapFromArgs(args);
90 for (auto i = map.begin(); i != map.end(); ++i) {
91 object->setProperty(i.key().toLatin1(), i.value());
92 }
93
94 auto result = store.create(*object).exec();
95 result.waitForFinished();
96 if (result.errorCode()) {
97 state.printError(QObject::tr("An error occurred while creating the entity: %1").arg(result.errorMessage()),
98 "akonaid_create_e" + QString::number(result.errorCode()));
99 }
100
101 return true;
102}
103
104
105Syntax::List syntax()
106{
107 Syntax::List syntax;
108
109 Syntax create("create", QObject::tr("Create items in a resource"), &SinkCreate::create);
110 create.children << Syntax("resource", QObject::tr("Creates a new resource"), &SinkCreate::resource);
111
112 syntax << create;
113 return syntax;
114}
115
116REGISTER_SYNTAX(SinkCreate)
117
118}
diff --git a/sinksh/syntax_modules/sink_list.cpp b/sinksh/syntax_modules/sink_list.cpp
new file mode 100644
index 0000000..9712b6f
--- /dev/null
+++ b/sinksh/syntax_modules/sink_list.cpp
@@ -0,0 +1,114 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "sinksh_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace SinkList
40{
41
42bool list(const QStringList &args, State &state)
43{
44 if (args.isEmpty()) {
45 state.printError(QObject::tr("Please provide at least one type to list (e.g. resource, .."));
46 return false;
47 }
48
49 auto resources = args;
50 auto type = !resources.isEmpty() ? resources.takeFirst() : QString();
51
52 if (!type.isEmpty() && !SinkshUtils::isValidStoreType(type)) {
53 state.printError(QObject::tr("Unknown type: %1").arg(type));
54 return false;
55 }
56
57 Sink::Query query;
58 for (const auto &res : resources) {
59 query.resources << res.toLatin1();
60 }
61 query.liveQuery = false;
62
63 QTime time;
64 time.start();
65 auto model = SinkshUtils::loadModel(type, query);
66 if (state.debugLevel() > 0) {
67 state.printLine(QObject::tr("Folder type %1").arg(type));
68 state.printLine(QObject::tr("Loaded model in %1 ms").arg(time.elapsed()));
69 }
70
71 //qDebug() << "Listing";
72 int colSize = 38; //Necessary to display a complete UUID
73 state.print(QObject::tr("Resource").leftJustified(colSize, ' ', true) +
74 QObject::tr("Identifier").leftJustified(colSize, ' ', true));
75 for (int i = 0; i < model->columnCount(QModelIndex()); i++) {
76 state.print(" | " + model->headerData(i, Qt::Horizontal).toString().leftJustified(colSize, ' ', true));
77 }
78 state.printLine();
79
80 QObject::connect(model.data(), &QAbstractItemModel::rowsInserted, [model, colSize, state](const QModelIndex &index, int start, int end) {
81 for (int i = start; i <= end; i++) {
82 auto object = model->data(model->index(i, 0, index), Sink::Store::DomainObjectBaseRole).value<Sink::ApplicationDomain::ApplicationDomainType::Ptr>();
83 state.print(object->resourceInstanceIdentifier().leftJustified(colSize, ' ', true));
84 state.print(object->identifier().leftJustified(colSize, ' ', true));
85 for (int col = 0; col < model->columnCount(QModelIndex()); col++) {
86 state.print(" | " + model->data(model->index(i, col, index)).toString().leftJustified(colSize, ' ', true));
87 }
88 state.printLine();
89 }
90 });
91
92 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [model, state](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
93 if (roles.contains(Sink::Store::ChildrenFetchedRole)) {
94 state.commandFinished();
95 }
96 });
97
98 if (!model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()) {
99 return true;
100 }
101
102 return false;
103}
104
105Syntax::List syntax()
106{
107 Syntax list("list", QObject::tr("List all resources, or the contents of one or more resources"), &SinkList::list, Syntax::EventDriven);
108 list.completer = &SinkshUtils::resourceOrTypeCompleter;
109 return Syntax::List() << list;
110}
111
112REGISTER_SYNTAX(SinkList)
113
114}
diff --git a/sinksh/syntax_modules/sink_modify.cpp b/sinksh/syntax_modules/sink_modify.cpp
new file mode 100644
index 0000000..4d637d8
--- /dev/null
+++ b/sinksh/syntax_modules/sink_modify.cpp
@@ -0,0 +1,120 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "sinksh_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace SinkModify
40{
41
42bool modify(const QStringList &args, State &state)
43{
44 if (args.isEmpty()) {
45 state.printError(QObject::tr("A type is required"), "sink_modify/02");
46 return false;
47 }
48
49 if (args.count() < 2) {
50 state.printError(QObject::tr("A resource ID is required to remove items"), "sink_modify/03");
51 return false;
52 }
53
54 if (args.count() < 3) {
55 state.printError(QObject::tr("An object ID is required to remove items"), "sink_modify/03");
56 return false;
57 }
58
59 auto type = args[0];
60 auto resourceId = args[1];
61 auto identifier = args[2];
62
63 auto &store = SinkshUtils::getStore(type);
64 Sink::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject(resourceId.toUtf8(), identifier.toUtf8());
65
66 auto map = SinkshUtils::keyValueMapFromArgs(args);
67 for (auto i = map.begin(); i != map.end(); ++i) {
68 object->setProperty(i.key().toLatin1(), i.value());
69 }
70
71 auto result = store.modify(*object).exec();
72 result.waitForFinished();
73 if (result.errorCode()) {
74 state.printError(QObject::tr("An error occurred while removing %1 from %1: %2").arg(identifier).arg(resourceId).arg(result.errorMessage()),
75 "akonaid__modify_e" + QString::number(result.errorCode()));
76 }
77
78 return true;
79}
80
81bool resource(const QStringList &args, State &state)
82{
83 if (args.isEmpty()) {
84 state.printError(QObject::tr("A resource can not be modified without an id"), "sink_modify/01");
85 }
86
87 auto &store = SinkshUtils::getStore("resource");
88
89 auto resourceId = args.at(0);
90 Sink::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject("", resourceId.toLatin1());
91
92 auto map = SinkshUtils::keyValueMapFromArgs(args);
93 for (auto i = map.begin(); i != map.end(); ++i) {
94 object->setProperty(i.key().toLatin1(), i.value());
95 }
96
97 auto result = store.modify(*object).exec();
98 result.waitForFinished();
99 if (result.errorCode()) {
100 state.printError(QObject::tr("An error occurred while modifying the resource %1: %2").arg(resourceId).arg(result.errorMessage()),
101 "akonaid_modify_e" + QString::number(result.errorCode()));
102 }
103
104 return true;
105}
106
107
108Syntax::List syntax()
109{
110 Syntax modify("modify", QObject::tr("Modify items in a resource"), &SinkModify::modify);
111 Syntax resource("resource", QObject::tr("Modify a resource"), &SinkModify::resource);//, Syntax::EventDriven);
112 resource.completer = &SinkshUtils::resourceOrTypeCompleter;
113 modify.children << resource;
114
115 return Syntax::List() << modify;
116}
117
118REGISTER_SYNTAX(SinkModify)
119
120}
diff --git a/sinksh/syntax_modules/sink_remove.cpp b/sinksh/syntax_modules/sink_remove.cpp
new file mode 100644
index 0000000..b374824
--- /dev/null
+++ b/sinksh/syntax_modules/sink_remove.cpp
@@ -0,0 +1,110 @@
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 <QCoreApplication>
21#include <QDebug>
22#include <QObject> // tr()
23#include <QModelIndex>
24#include <QTime>
25
26#include "common/resource.h"
27#include "common/storage.h"
28#include "common/domain/event.h"
29#include "common/domain/folder.h"
30#include "common/resourceconfig.h"
31#include "common/log.h"
32#include "common/storage.h"
33#include "common/definitions.h"
34
35#include "sinksh_utils.h"
36#include "state.h"
37#include "syntaxtree.h"
38
39namespace SinkRemove
40{
41
42bool remove(const QStringList &args, State &state)
43{
44 if (args.isEmpty()) {
45 state.printError(QObject::tr("A type is required"), "sink_remove/02");
46 return false;
47 }
48
49 if (args.count() < 2) {
50 state.printError(QObject::tr("A resource ID is required to remove items"), "sink_remove/03");
51 return false;
52 }
53
54 if (args.count() < 3) {
55 state.printError(QObject::tr("An object ID is required to remove items"), "sink_remove/03");
56 return false;
57 }
58
59 auto type = args[0];
60 auto resourceId = args[1];
61 auto identifier = args[2];
62
63 auto &store = SinkshUtils::getStore(type);
64 Sink::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject(resourceId.toUtf8(), identifier.toUtf8());
65
66 auto result = store.remove(*object).exec();
67 result.waitForFinished();
68 if (result.errorCode()) {
69 state.printError(QObject::tr("An error occurred while removing %1 from %1: %2").arg(identifier).arg(resourceId).arg(result.errorMessage()),
70 "akonaid_remove_e" + QString::number(result.errorCode()));
71 }
72
73 return true;
74}
75
76bool resource(const QStringList &args, State &state)
77{
78 if (args.isEmpty()) {
79 state.printError(QObject::tr("A resource can not be removed without an id"), "sink_remove/01");
80 }
81
82 auto &store = SinkshUtils::getStore("resource");
83
84 auto resourceId = args.at(0);
85 Sink::ApplicationDomain::ApplicationDomainType::Ptr object = store.getObject("", resourceId.toLatin1());
86
87 auto result = store.remove(*object).exec();
88 result.waitForFinished();
89 if (result.errorCode()) {
90 state.printError(QObject::tr("An error occurred while removing the resource %1: %2").arg(resourceId).arg(result.errorMessage()),
91 "akonaid_remove_e" + QString::number(result.errorCode()));
92 }
93
94 return true;
95}
96
97
98Syntax::List syntax()
99{
100 Syntax remove("remove", QObject::tr("Remove items in a resource"), &SinkRemove::remove);
101 Syntax resource("resource", QObject::tr("Removes a resource"), &SinkRemove::resource);//, Syntax::EventDriven);
102 resource.completer = &SinkshUtils::resourceCompleter;
103 remove.children << resource;
104
105 return Syntax::List() << remove;
106}
107
108REGISTER_SYNTAX(SinkRemove)
109
110}
diff --git a/sinksh/syntax_modules/sink_stat.cpp b/sinksh/syntax_modules/sink_stat.cpp
new file mode 100644
index 0000000..06586d9
--- /dev/null
+++ b/sinksh/syntax_modules/sink_stat.cpp
@@ -0,0 +1,120 @@
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 <QDebug>
21#include <QObject> // tr()
22#include <QTimer>
23#include <QDir>
24
25#include "common/resource.h"
26#include "common/storage.h"
27#include "common/domain/event.h"
28#include "common/domain/folder.h"
29#include "common/resourceconfig.h"
30#include "common/log.h"
31#include "common/storage.h"
32#include "common/definitions.h"
33
34#include "sinksh_utils.h"
35#include "state.h"
36#include "syntaxtree.h"
37
38namespace SinkStat
39{
40
41void statResources(const QStringList &resources, const State &state)
42{
43 qint64 total = 0;
44 for (const auto &resource : resources) {
45 Sink::Storage storage(Sink::storageLocation(), resource, Sink::Storage::ReadOnly);
46 auto transaction = storage.createTransaction(Sink::Storage::ReadOnly);
47
48 QList<QByteArray> databases = transaction.getDatabaseNames();
49 for (const auto &databaseName : databases) {
50 state.printLine(QObject::tr("Database: %1").arg(QString(databaseName)), 1);
51 auto db = transaction.openDatabase(databaseName);
52 qint64 size = db.getSize() / 1024;
53 state.printLine(QObject::tr("Size [kb]: %1").arg(size), 1);
54 total += size;
55 }
56 int diskUsage = 0;
57
58 QDir dir(Sink::storageLocation());
59 for (const auto &folder : dir.entryList(QStringList() << resource + "*")) {
60 diskUsage += Sink::Storage(Sink::storageLocation(), folder, Sink::Storage::ReadOnly).diskUsage();
61 }
62 auto size = diskUsage / 1024;
63 state.printLine(QObject::tr("Disk usage [kb]: %1").arg(size), 1);
64 }
65
66 state.printLine(QObject::tr("Total [kb]: %1").arg(total));
67}
68
69bool statAllResources(State &state)
70{
71 Sink::Query query;
72 query.liveQuery = false;
73 auto model = SinkshUtils::loadModel("resource", query);
74
75 //SUUUPER ugly, but can't think of a better way with 2 glasses of wine in me on Christmas day
76 static QStringList resources;
77 resources.clear();
78
79 QObject::connect(model.data(), &QAbstractItemModel::rowsInserted, [model](const QModelIndex &index, int start, int end) mutable {
80 for (int i = start; i <= end; i++) {
81 auto object = model->data(model->index(i, 0, index), Sink::Store::DomainObjectBaseRole).value<Sink::ApplicationDomain::ApplicationDomainType::Ptr>();
82 resources << object->identifier();
83 }
84 });
85
86 QObject::connect(model.data(), &QAbstractItemModel::dataChanged, [model, state](const QModelIndex &, const QModelIndex &, const QVector<int> &roles) {
87 if (roles.contains(Sink::Store::ChildrenFetchedRole)) {
88 statResources(resources, state);
89 state.commandFinished();
90 }
91 });
92
93 if (!model->data(QModelIndex(), Sink::Store::ChildrenFetchedRole).toBool()) {
94 return true;
95 }
96
97 return false;
98}
99
100bool stat(const QStringList &args, State &state)
101{
102 if (args.isEmpty()) {
103 return statAllResources(state);
104 }
105
106 statResources(args, state);
107 return false;
108}
109
110Syntax::List syntax()
111{
112 Syntax state("stat", QObject::tr("Shows database usage for the resources requested"), &SinkStat::stat, Syntax::EventDriven);
113 state.completer = &SinkshUtils::resourceCompleter;
114
115 return Syntax::List() << state;
116}
117
118REGISTER_SYNTAX(SinkStat)
119
120}
diff --git a/sinksh/syntax_modules/sink_sync.cpp b/sinksh/syntax_modules/sink_sync.cpp
new file mode 100644
index 0000000..3006202
--- /dev/null
+++ b/sinksh/syntax_modules/sink_sync.cpp
@@ -0,0 +1,67 @@
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 <QDebug>
21#include <QObject> // tr()
22#include <QTimer>
23
24#include "common/resource.h"
25#include "common/storage.h"
26#include "common/domain/event.h"
27#include "common/domain/folder.h"
28#include "common/resourceconfig.h"
29#include "common/log.h"
30#include "common/storage.h"
31#include "common/definitions.h"
32
33#include "sinksh_utils.h"
34#include "state.h"
35#include "syntaxtree.h"
36
37namespace SinkSync
38{
39
40bool sync(const QStringList &args, State &state)
41{
42 Sink::Query query;
43 for (const auto &res : args) {
44 query.resources << res.toLatin1();
45 }
46
47 QTimer::singleShot(0, [query, state]() {
48 Sink::Store::synchronize(query).then<void>([state]() {
49 state.printLine("Synchronization complete!");
50 state.commandFinished();
51 }).exec();
52 });
53
54 return true;
55}
56
57Syntax::List syntax()
58{
59 Syntax sync("sync", QObject::tr("Syncronizes all resources that are listed; and empty list triggers a syncronizaton on all resources"), &SinkSync::sync, Syntax::EventDriven );
60 sync.completer = &SinkshUtils::resourceCompleter;
61
62 return Syntax::List() << sync;
63}
64
65REGISTER_SYNTAX(SinkSync)
66
67}
diff --git a/sinksh/syntaxtree.cpp b/sinksh/syntaxtree.cpp
new file mode 100644
index 0000000..4860582
--- /dev/null
+++ b/sinksh/syntaxtree.cpp
@@ -0,0 +1,221 @@
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 "syntaxtree.h"
21
22#include <QCoreApplication>
23#include <QDebug>
24
25SyntaxTree *SyntaxTree::s_module = 0;
26
27Syntax::Syntax()
28{
29}
30
31Syntax::Syntax(const QString &k, const QString &helpText, std::function<bool(const QStringList &, State &)> l, Interactivity inter)
32 : keyword(k),
33 help(helpText),
34 interactivity(inter),
35 lambda(l)
36{
37}
38
39SyntaxTree::SyntaxTree()
40{
41}
42
43int SyntaxTree::registerSyntax(std::function<Syntax::List()> f)
44{
45 m_syntax += f();
46 return m_syntax.size();
47}
48
49SyntaxTree *SyntaxTree::self()
50{
51 if (!s_module) {
52 s_module = new SyntaxTree;
53 }
54
55 return s_module;
56}
57
58Syntax::List SyntaxTree::syntax() const
59{
60 return m_syntax;
61}
62
63bool SyntaxTree::run(const QStringList &commands)
64{
65 bool success = false;
66 m_timeElapsed.start();
67 Command command = match(commands);
68 if (command.first) {
69 if (command.first->lambda) {
70 success = command.first->lambda(command.second, m_state);
71 if (success && command.first->interactivity == Syntax::EventDriven) {
72 success = m_state.commandStarted();
73 }
74 } else if (command.first->children.isEmpty()) {
75 m_state.printError(QObject::tr("Broken command... sorry :("), "st_broken");
76 } else {
77 QStringList keywordList;
78 for (auto syntax: command.first->children) {
79 keywordList << syntax.keyword;
80 }
81 const QString keywords = keywordList.join(" " );
82 m_state.printError(QObject::tr("Command requires additional arguments, one of: %1").arg(keywords));
83 }
84 } else {
85 m_state.printError(QObject::tr("Unknown command"), "st_unknown");
86 }
87
88 if (m_state.commandTiming()) {
89 m_state.printLine(QObject::tr("Time elapsed: %1").arg(m_timeElapsed.elapsed()));
90 }
91 return false;
92}
93
94SyntaxTree::Command SyntaxTree::match(const QStringList &commandLine) const
95{
96 if (commandLine.isEmpty()) {
97 return Command();
98 }
99
100 QStringListIterator commandLineIt(commandLine);
101
102 QVectorIterator<Syntax> syntaxIt(m_syntax);
103 const Syntax *lastFullSyntax = 0;
104 QStringList tailCommands;
105 while (commandLineIt.hasNext() && syntaxIt.hasNext()) {
106 const QString word = commandLineIt.next();
107 while (syntaxIt.hasNext()) {
108 const Syntax &syntax = syntaxIt.next();
109 if (word == syntax.keyword) {
110 lastFullSyntax = &syntax;
111 syntaxIt = syntax.children;
112 break;
113 }
114 }
115 }
116
117 if (lastFullSyntax) {
118 while (commandLineIt.hasNext()) {
119 tailCommands << commandLineIt.next();
120 }
121
122 return std::make_pair(lastFullSyntax, tailCommands);
123 }
124
125 return Command();
126}
127
128Syntax::List SyntaxTree::nearestSyntax(const QStringList &words, const QString &fragment) const
129{
130 Syntax::List matches;
131
132 //qDebug() << "words are" << words;
133 if (words.isEmpty()) {
134 for (const Syntax &syntax: m_syntax) {
135 if (syntax.keyword.startsWith(fragment)) {
136 matches.push_back(syntax);
137 }
138 }
139 } else {
140 QStringListIterator wordIt(words);
141 QVectorIterator<Syntax> syntaxIt(m_syntax);
142 Syntax lastFullSyntax;
143
144 while (wordIt.hasNext()) {
145 const QString &word = wordIt.next();
146 while (syntaxIt.hasNext()) {
147 const Syntax &syntax = syntaxIt.next();
148 if (word == syntax.keyword) {
149 lastFullSyntax = syntax;
150 syntaxIt = syntax.children;
151 break;
152 }
153 }
154 }
155
156 //qDebug() << "exiting with" << lastFullSyntax.keyword << words.last();
157 if (lastFullSyntax.keyword == words.last()) {
158 syntaxIt = lastFullSyntax.children;
159 while (syntaxIt.hasNext()) {
160 Syntax syntax = syntaxIt.next();
161 if (fragment.isEmpty() || syntax.keyword.startsWith(fragment)) {
162 matches.push_back(syntax);
163 }
164 }
165 }
166 }
167
168 return matches;
169}
170
171State &SyntaxTree::state()
172{
173 return m_state;
174}
175
176QStringList SyntaxTree::tokenize(const QString &text)
177{
178 //TODO: properly tokenize (e.g. "foo bar" should not become ['"foo', 'bar"']a
179 static const QVector<QChar> quoters = QVector<QChar>() << '"' << '\'';
180 QStringList tokens;
181 QString acc;
182 QChar closer;
183 for (int i = 0; i < text.size(); ++i) {
184 const QChar c = text.at(i);
185 if (c == '\\') {
186 ++i;
187 if (i < text.size()) {
188 acc.append(text.at(i));
189 }
190 } else if (!closer.isNull()) {
191 if (c == closer) {
192 acc = acc.trimmed();
193 if (!acc.isEmpty()) {
194 tokens << acc;
195 }
196 acc.clear();
197 closer = QChar();
198 } else {
199 acc.append(c);
200 }
201 } else if (c.isSpace()) {
202 acc = acc.trimmed();
203 if (!acc.isEmpty()) {
204 tokens << acc;
205 }
206 acc.clear();
207 } else if (quoters.contains(c)) {
208 closer = c;
209 } else {
210 acc.append(c);
211 }
212 }
213
214 acc = acc.trimmed();
215 if (!acc.isEmpty()) {
216 tokens << acc;
217 }
218
219 return tokens;
220}
221
diff --git a/sinksh/syntaxtree.h b/sinksh/syntaxtree.h
new file mode 100644
index 0000000..468aad3
--- /dev/null
+++ b/sinksh/syntaxtree.h
@@ -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#pragma once
21
22#include "state.h"
23
24#include <QStringList>
25#include <QTime>
26#include <QVector>
27
28#include <functional>
29
30class Syntax
31{
32public:
33 typedef QVector<Syntax> List;
34
35 enum Interactivity {
36 NotInteractive = 0,
37 EventDriven
38 };
39
40 Syntax();
41 Syntax(const QString &keyword,
42 const QString &helpText = QString(),
43 std::function<bool(const QStringList &, State &)> lambda = std::function<bool(const QStringList &, State &)>(),
44 Interactivity interactivity = NotInteractive);
45
46 QString keyword;
47 QString help;
48 Interactivity interactivity;
49 std::function<bool(const QStringList &, State &)> lambda;
50 std::function<QStringList(const QStringList &, const QString &, State &state)> completer;
51
52 QVector<Syntax> children;
53};
54
55class SyntaxTree
56{
57public:
58 typedef std::pair<const Syntax *, QStringList> Command;
59
60 static SyntaxTree *self();
61
62 int registerSyntax(std::function<Syntax::List()> f);
63 Syntax::List syntax() const;
64 Command match(const QStringList &commands) const;
65 Syntax::List nearestSyntax(const QStringList &words, const QString &fragment) const;
66 State &state();
67 bool run(const QStringList &commands);
68
69 static QStringList tokenize(const QString &text);
70
71private:
72 SyntaxTree();
73
74 Syntax::List m_syntax;
75 State m_state;
76 QTime m_timeElapsed;
77 static SyntaxTree *s_module;
78};
79
80#define REGISTER_SYNTAX(name) static const int theTrickFor##name = SyntaxTree::self()->registerSyntax(&name::syntax);
diff --git a/sinksh/utils.cpp b/sinksh/utils.cpp
new file mode 100644
index 0000000..d2a28ed
--- /dev/null
+++ b/sinksh/utils.cpp
@@ -0,0 +1,42 @@
1/*
2 * Copyright (C) 2016 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 "utils.h"
21
22namespace Utils
23{
24
25QStringList filteredCompletions(const QStringList &possibleCompletions, const QString &commandFragment, Qt::CaseSensitivity cs)
26{
27 if (commandFragment.isEmpty()) {
28 return possibleCompletions;
29 }
30
31 QStringList filtered;
32 for (auto item: possibleCompletions) {
33 if (item.startsWith(commandFragment, cs)) {
34 filtered << item;
35 }
36 }
37
38 return filtered;
39}
40
41} // namespace Utils
42
diff --git a/sinksh/utils.h b/sinksh/utils.h
new file mode 100644
index 0000000..82be8d5
--- /dev/null
+++ b/sinksh/utils.h
@@ -0,0 +1,30 @@
1/*
2 * Copyright (C) 2016 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
24namespace Utils
25{
26
27QStringList filteredCompletions(const QStringList &possibleCompletions, const QString &commandFragment, Qt::CaseSensitivity cs = Qt::CaseSensitive);
28
29} // namespace Utils
30