diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-01-20 19:07:07 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2016-01-20 19:07:07 +0100 |
commit | bdb01c2c068df326f5a8328ed1492ab1bea388c5 (patch) | |
tree | 25c2ee1b29bc481b6914c244ed9ca194b1415d16 /sinksh | |
parent | 17e7ee40c9185c0505883853345fd6024c675b1a (diff) | |
download | sink-bdb01c2c068df326f5a8328ed1492ab1bea388c5.tar.gz sink-bdb01c2c068df326f5a8328ed1492ab1bea388c5.zip |
Renamed Akonadi2 to Sink
(except for documentation).
Diffstat (limited to 'sinksh')
-rw-r--r-- | sinksh/CMakeLists.txt | 29 | ||||
-rw-r--r-- | sinksh/TODO | 11 | ||||
-rw-r--r-- | sinksh/main.cpp | 115 | ||||
-rw-r--r-- | sinksh/repl/repl.cpp | 91 | ||||
-rw-r--r-- | sinksh/repl/repl.h | 35 | ||||
-rw-r--r-- | sinksh/repl/replStates.cpp | 171 | ||||
-rw-r--r-- | sinksh/repl/replStates.h | 87 | ||||
-rw-r--r-- | sinksh/sinksh_utils.cpp | 134 | ||||
-rw-r--r-- | sinksh/sinksh_utils.h | 88 | ||||
-rw-r--r-- | sinksh/state.cpp | 144 | ||||
-rw-r--r-- | sinksh/state.h | 52 | ||||
-rw-r--r-- | sinksh/syntax_modules/core_syntax.cpp | 204 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_clear.cpp | 61 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_count.cpp | 83 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_create.cpp | 118 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_list.cpp | 114 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_modify.cpp | 120 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_remove.cpp | 110 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_stat.cpp | 120 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_sync.cpp | 67 | ||||
-rw-r--r-- | sinksh/syntaxtree.cpp | 221 | ||||
-rw-r--r-- | sinksh/syntaxtree.h | 80 | ||||
-rw-r--r-- | sinksh/utils.cpp | 42 | ||||
-rw-r--r-- | sinksh/utils.h | 30 |
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 @@ | |||
1 | project(sinksh) | ||
2 | |||
3 | find_package(Readline REQUIRED) | ||
4 | |||
5 | |||
6 | set(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 | |||
24 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) | ||
25 | |||
26 | add_executable(${PROJECT_NAME} ${sink_cli_SRCS}) | ||
27 | target_link_libraries(${PROJECT_NAME} Qt5::Core ${Readline_LIBRARY} sinkcommon) | ||
28 | install(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 | |||
40 | int 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 | |||
58 | bool goInteractive(const QStringList &, State &) | ||
59 | { | ||
60 | enterRepl(); | ||
61 | return true; | ||
62 | } | ||
63 | |||
64 | Syntax::List goInteractiveSyntax() | ||
65 | { | ||
66 | Syntax interactive("go_interactive", QString(), &goInteractive); | ||
67 | return Syntax::List() << interactive; | ||
68 | } | ||
69 | |||
70 | void 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 | |||
85 | int 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 | |||
33 | Repl::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 | |||
65 | Repl::~Repl() | ||
66 | { | ||
67 | // readline history writing | ||
68 | write_history(commandHistoryPath().toLocal8Bit()); | ||
69 | } | ||
70 | |||
71 | void 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 | |||
79 | QString 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 | |||
24 | class Repl : public QStateMachine | ||
25 | { | ||
26 | Q_OBJECT | ||
27 | |||
28 | public: | ||
29 | Repl(QObject *parent = 0); | ||
30 | ~Repl(); | ||
31 | |||
32 | private: | ||
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 | |||
34 | static char *sink_cli_next_tab_complete_match(const char *text, int state); | ||
35 | static char ** sink_cli_tab_completion(const char *text, int start, int end); | ||
36 | |||
37 | ReadState::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 | |||
44 | void 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 | |||
68 | const char *ReadState::prompt() const | ||
69 | { | ||
70 | return "> "; | ||
71 | } | ||
72 | |||
73 | UnfinishedReadState::UnfinishedReadState(QState *parent) | ||
74 | : ReadState(parent) | ||
75 | { | ||
76 | } | ||
77 | |||
78 | const char *UnfinishedReadState::prompt() const | ||
79 | { | ||
80 | return " "; | ||
81 | } | ||
82 | |||
83 | EvalState::EvalState(QState *parent) | ||
84 | : QState(parent) | ||
85 | { | ||
86 | } | ||
87 | |||
88 | void 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 | |||
108 | void 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 | |||
122 | PrintState::PrintState(QState *parent) | ||
123 | : QState(parent) | ||
124 | { | ||
125 | } | ||
126 | |||
127 | void 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 | |||
140 | static QStringList tab_completion_full_state; | ||
141 | static bool tab_completion_at_root = false; | ||
142 | |||
143 | static 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 | |||
150 | static 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 | |||
24 | class QSocketNotifier; | ||
25 | |||
26 | class ReadState : public QState | ||
27 | { | ||
28 | Q_OBJECT | ||
29 | |||
30 | public: | ||
31 | ReadState(QState *parent = 0); | ||
32 | |||
33 | Q_SIGNALS: | ||
34 | void command(const QString &command); | ||
35 | void exitRequested(); | ||
36 | |||
37 | protected: | ||
38 | void onEntry(QEvent *event); | ||
39 | virtual const char *prompt() const; | ||
40 | }; | ||
41 | |||
42 | class UnfinishedReadState : public ReadState | ||
43 | { | ||
44 | Q_OBJECT | ||
45 | |||
46 | public: | ||
47 | UnfinishedReadState(QState *parent = 0); | ||
48 | |||
49 | protected: | ||
50 | const char *prompt() const; | ||
51 | }; | ||
52 | |||
53 | class EvalState : public QState | ||
54 | { | ||
55 | Q_OBJECT | ||
56 | |||
57 | public: | ||
58 | EvalState(QState *parent = 0); | ||
59 | |||
60 | Q_SIGNALS: | ||
61 | void completed(); | ||
62 | void continueInput(); | ||
63 | void output(const QString &output); | ||
64 | |||
65 | protected: | ||
66 | void onEntry(QEvent *event); | ||
67 | |||
68 | private: | ||
69 | void complete(); | ||
70 | |||
71 | QString m_partial; | ||
72 | }; | ||
73 | |||
74 | class PrintState : public QState | ||
75 | { | ||
76 | Q_OBJECT | ||
77 | |||
78 | public: | ||
79 | PrintState(QState *parent = 0); | ||
80 | |||
81 | Q_SIGNALS: | ||
82 | void completed(); | ||
83 | |||
84 | protected: | ||
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 | |||
27 | namespace SinkshUtils | ||
28 | { | ||
29 | |||
30 | static QStringList s_types = QStringList() << "resource" << "folder" << "mail" << "event"; | ||
31 | |||
32 | bool isValidStoreType(const QString &type) | ||
33 | { | ||
34 | return s_types.contains(type); | ||
35 | } | ||
36 | |||
37 | StoreBase &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 | |||
60 | QSharedPointer<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 | |||
76 | QStringList 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 | |||
101 | QStringList resourceCompleter(const QStringList &, const QString &fragment, State &state) | ||
102 | { | ||
103 | return Utils::filteredCompletions(resourceIds(state), fragment); | ||
104 | } | ||
105 | |||
106 | QStringList 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 | |||
116 | QStringList typeCompleter(const QStringList &commands, const QString &fragment, State &state) | ||
117 | { | ||
118 | return Utils::filteredCompletions(s_types, fragment); | ||
119 | } | ||
120 | |||
121 | QMap<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 | |||
31 | namespace SinkshUtils | ||
32 | { | ||
33 | |||
34 | class StoreBase; | ||
35 | |||
36 | bool isValidStoreType(const QString &type); | ||
37 | StoreBase &getStore(const QString &type); | ||
38 | QSharedPointer<QAbstractItemModel> loadModel(const QString &type, Sink::Query query); | ||
39 | QStringList resourceIds(); | ||
40 | QStringList resourceCompleter(const QStringList &, const QString &fragment, State &state); | ||
41 | QStringList resourceOrTypeCompleter(const QStringList &commands, const QString &fragment, State &state); | ||
42 | QStringList typeCompleter(const QStringList &commands, const QString &fragment, State &state); | ||
43 | QMap<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 | */ | ||
48 | class StoreBase { | ||
49 | public: | ||
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 | |||
58 | template <typename T> | ||
59 | class Store : public StoreBase { | ||
60 | public: | ||
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 | |||
29 | static bool s_hasEventLoop = false; | ||
30 | |||
31 | class State::Private | ||
32 | { | ||
33 | public: | ||
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 | |||
54 | State::State() | ||
55 | : d(new Private) | ||
56 | { | ||
57 | } | ||
58 | |||
59 | void 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 | |||
68 | void State::printLine(const QString &message, unsigned int indentationLevel) const | ||
69 | { | ||
70 | print(message, indentationLevel); | ||
71 | d->outStream << "\n"; | ||
72 | d->outStream.flush(); | ||
73 | } | ||
74 | |||
75 | void State::printError(const QString &errorMessage, const QString &errorCode) const | ||
76 | { | ||
77 | printLine("ERROR" + (errorCode.isEmpty() ? "" : " " + errorCode) + ": " + errorMessage); | ||
78 | } | ||
79 | |||
80 | void State::setDebugLevel(unsigned int level) | ||
81 | { | ||
82 | if (level < 7) { | ||
83 | d->debugLevel = level; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | unsigned int State::debugLevel() const | ||
88 | { | ||
89 | return d->debugLevel; | ||
90 | } | ||
91 | |||
92 | int 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 | |||
103 | void State::commandFinished(int returnCode) const | ||
104 | { | ||
105 | if (!s_hasEventLoop) { | ||
106 | QCoreApplication::exit(returnCode); | ||
107 | } else { | ||
108 | d->eventLoop()->exit(returnCode); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | void State::setHasEventLoop(bool evented) | ||
113 | { | ||
114 | s_hasEventLoop = evented; | ||
115 | } | ||
116 | |||
117 | bool State::hasEventLoop() | ||
118 | { | ||
119 | return s_hasEventLoop; | ||
120 | } | ||
121 | |||
122 | void State::setCommandTiming(bool time) | ||
123 | { | ||
124 | d->timing = time; | ||
125 | } | ||
126 | |||
127 | bool State::commandTiming() const | ||
128 | { | ||
129 | return d->timing; | ||
130 | } | ||
131 | |||
132 | void State::setLoggingLevel(const QString &level) const | ||
133 | { | ||
134 | Sink::Log::setDebugOutputLevel(Sink::Log::debugLevelFromName(level.toLatin1())); | ||
135 | } | ||
136 | |||
137 | QString 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 | |||
24 | class State | ||
25 | { | ||
26 | public: | ||
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 | |||
48 | private: | ||
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 | |||
29 | namespace CoreSyntax | ||
30 | { | ||
31 | |||
32 | bool exit(const QStringList &, State &) | ||
33 | { | ||
34 | ::exit(0); | ||
35 | return true; | ||
36 | } | ||
37 | |||
38 | bool 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 | |||
80 | QStringList 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 | |||
95 | bool 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 | |||
114 | bool printDebugLevel(const QStringList &, State &state) | ||
115 | { | ||
116 | state.printLine(QString::number(state.debugLevel())); | ||
117 | return true; | ||
118 | } | ||
119 | |||
120 | bool printCommandTiming(const QStringList &, State &state) | ||
121 | { | ||
122 | state.printLine(state.commandTiming() ? QObject::tr("on") : QObject::tr("off")); | ||
123 | return true; | ||
124 | } | ||
125 | |||
126 | void 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 | |||
143 | bool printSyntaxTree(const QStringList &, State &state) | ||
144 | { | ||
145 | printSyntaxBranch(state, SyntaxTree::self()->syntax(), 0); | ||
146 | return true; | ||
147 | } | ||
148 | |||
149 | bool 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 | |||
160 | bool printLoggingLevel(const QStringList &commands, State &state) | ||
161 | { | ||
162 | const QString level = state.loggingLevel(); | ||
163 | state.printLine(level); | ||
164 | return true; | ||
165 | } | ||
166 | |||
167 | Syntax::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 | |||
201 | REGISTER_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 | |||
37 | namespace SinkClear | ||
38 | { | ||
39 | |||
40 | bool 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 | |||
51 | Syntax::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 | |||
59 | REGISTER_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 | |||
39 | namespace SinkCount | ||
40 | { | ||
41 | |||
42 | bool 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 | |||
73 | Syntax::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 | |||
81 | REGISTER_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 | |||
39 | namespace SinkCreate | ||
40 | { | ||
41 | |||
42 | bool 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 | |||
76 | bool 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 | |||
105 | Syntax::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 | |||
116 | REGISTER_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 | |||
39 | namespace SinkList | ||
40 | { | ||
41 | |||
42 | bool 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 | |||
105 | Syntax::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 | |||
112 | REGISTER_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 | |||
39 | namespace SinkModify | ||
40 | { | ||
41 | |||
42 | bool 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 | |||
81 | bool 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 | |||
108 | Syntax::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 | |||
118 | REGISTER_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 | |||
39 | namespace SinkRemove | ||
40 | { | ||
41 | |||
42 | bool 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 | |||
76 | bool 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 | |||
98 | Syntax::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 | |||
108 | REGISTER_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 | |||
38 | namespace SinkStat | ||
39 | { | ||
40 | |||
41 | void 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 | |||
69 | bool 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 | |||
100 | bool 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 | |||
110 | Syntax::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 | |||
118 | REGISTER_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 | |||
37 | namespace SinkSync | ||
38 | { | ||
39 | |||
40 | bool 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 | |||
57 | Syntax::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 | |||
65 | REGISTER_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 | |||
25 | SyntaxTree *SyntaxTree::s_module = 0; | ||
26 | |||
27 | Syntax::Syntax() | ||
28 | { | ||
29 | } | ||
30 | |||
31 | Syntax::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 | |||
39 | SyntaxTree::SyntaxTree() | ||
40 | { | ||
41 | } | ||
42 | |||
43 | int SyntaxTree::registerSyntax(std::function<Syntax::List()> f) | ||
44 | { | ||
45 | m_syntax += f(); | ||
46 | return m_syntax.size(); | ||
47 | } | ||
48 | |||
49 | SyntaxTree *SyntaxTree::self() | ||
50 | { | ||
51 | if (!s_module) { | ||
52 | s_module = new SyntaxTree; | ||
53 | } | ||
54 | |||
55 | return s_module; | ||
56 | } | ||
57 | |||
58 | Syntax::List SyntaxTree::syntax() const | ||
59 | { | ||
60 | return m_syntax; | ||
61 | } | ||
62 | |||
63 | bool 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 | |||
94 | SyntaxTree::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 | |||
128 | Syntax::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 | |||
171 | State &SyntaxTree::state() | ||
172 | { | ||
173 | return m_state; | ||
174 | } | ||
175 | |||
176 | QStringList 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 | |||
30 | class Syntax | ||
31 | { | ||
32 | public: | ||
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 | |||
55 | class SyntaxTree | ||
56 | { | ||
57 | public: | ||
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 | |||
71 | private: | ||
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 | |||
22 | namespace Utils | ||
23 | { | ||
24 | |||
25 | QStringList 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 | |||
24 | namespace Utils | ||
25 | { | ||
26 | |||
27 | QStringList filteredCompletions(const QStringList &possibleCompletions, const QString &commandFragment, Qt::CaseSensitivity cs = Qt::CaseSensitive); | ||
28 | |||
29 | } // namespace Utils | ||
30 | |||