1 /*
2  * Copyright (C) 2008 Martin Hostettler
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (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, see <http://www.gnu.org/licenses/>.
16  *
17  */
18 
19 // manager mini command system
20 
21 #include <QDebug>
22 
23 #include "mcmdmanager.h"
24 
25 
MCmdSimpleState(QString name,QString prompt)26 MCmdSimpleState::MCmdSimpleState(QString name, QString prompt) {
27 	name_ = name;
28 	prompt_ = prompt;
29 }
30 
MCmdSimpleState(QString name,QString prompt,int flags)31 MCmdSimpleState::MCmdSimpleState(QString name, QString prompt, int flags) {
32 	name_ = name;
33 	prompt_ = prompt;
34 	flags_ = flags;
35 }
36 
37 
~MCmdSimpleState()38 MCmdSimpleState::~MCmdSimpleState() {
39 }
40 
MCmdManager(MCmdUiSiteIface * site_)41 MCmdManager::MCmdManager(MCmdUiSiteIface* site_) : state_(0), uiSite_(site_) {
42 };
43 
~MCmdManager()44 MCmdManager::~MCmdManager() {
45 	foreach(MCmdProviderIface *prov, providers_) {
46 		prov->mCmdSiteDestroyed();
47 	}
48 }
49 
50 
parseCommand(const QString command,int pos,int & part,QString & partial,int & start,int & end,char & quotedAtPos)51 QStringList MCmdManager::parseCommand(const QString command, int pos, int &part, QString &partial, int &start, int &end, char &quotedAtPos)
52 {
53 	QStringList list;
54 	QString item;
55 	bool escape=false;
56 	int quote=0; // "=1, '=2
57 	bool space=true;
58 
59 	int partStart = 0;
60 	quotedAtPos = 0;
61 	for (int i=0; i < command.length(); i++) {
62 		if (i == pos) {
63 			part = list.size();
64 			partial = item;
65 			end = i;
66 			start = partStart;
67 			if (quote) quotedAtPos =  (quote == 1) ? '"' : '\'';
68 		}
69 
70 
71 		QChar ch = command[i];
72 		if (escape) {
73 			escape = false;
74 			item += ch;
75 		} else if (ch == '\\') {
76 			escape = true;
77 		} else if (quote != 0) {
78 			if ((quote == 1 && ch == '"') || (quote == 2 && ch == '\'')) {
79 				quote = 0;
80 			} else {
81 				item += ch;
82 			}
83 		} else if (ch == ' ') {
84 			partStart = i+1;
85 			if (space) {
86 				continue;
87 			}
88 			list << item;
89 			item = "";
90 			space = true;
91 			continue;
92 		} else if (ch == '\'') {
93 			quote = 2;
94 		} else if (ch == '"') {
95 			quote = 1;
96 		} else {
97 			item += ch;
98 		}
99 		space = false;
100 	}
101 	if (command.length() == pos) {
102 		part = list.size();
103 		partial = item;
104 		end = command.length();
105 		start = partStart;
106 		if (quote) quotedAtPos =  (quote == 1) ? '"' : '\'';
107 	}
108 
109 	if (!space) list << item;
110 	return list;
111 }
112 
serializeCommand(const QStringList & list)113 QString MCmdManager::serializeCommand(const QStringList &list)
114 {
115 	QString retval;
116 	bool needspace = false;
117 	QRegExp specials("([\"\'\\\\ ])");
118 	foreach(QString item, list) {
119 		item.replace(specials, "\\\\1");
120 		if (item == "") item = "\"\"";
121 		if (needspace) retval += " ";
122 		retval += item;
123 		needspace = true;
124 	}
125 	return retval;
126 }
127 
128 
processCommand(QString command)129 bool MCmdManager::processCommand(QString command) {
130 	MCmdStateIface *tmpstate=0;
131 	QStringList preset;
132 	QStringList items;
133 	if (state_->getFlags() & MCMDSTATE_UNPARSED) {
134 		items << command;
135 	} else {
136 		int tmp_1;
137 		QString tmp_2;
138 		char tmp_3;
139 		items = parseCommand(command, -1, tmp_1, tmp_2, tmp_1, tmp_1, tmp_3);
140 	}
141 	foreach(MCmdProviderIface *prov, providers_) {
142 		if (prov->mCmdTryStateTransit(state_, items, tmpstate, preset)) {
143 			state_ = tmpstate;
144 			if (state_ != 0) {
145 				QString prompt = state_->getPrompt();
146 				QString def;
147 				if (state_->getFlags() & MCMDSTATE_UNPARSED) {
148 					if (preset.size() == 1) {
149 						def = preset.at(0);
150 					}
151 				} else {
152 					def = serializeCommand(preset);
153 				}
154 				uiSite_->mCmdReady(prompt, def);
155 			} else {
156 				uiSite_->mCmdClose();
157 			}
158 			return true;
159 		}
160 	}
161 
162 	tmpstate = state_;
163 	bool ret = state_->unhandled(items);
164 	state_ = 0;
165 	if (state_ == 0) {
166 		tmpstate->dispose();
167 		uiSite_->mCmdClose();
168 	}
169 	return ret;
170 }
171 
172 
open(MCmdStateIface * state,QStringList preset)173 bool MCmdManager::open(MCmdStateIface *state, QStringList preset) {
174 	if (0 != state_) state_->dispose();
175 
176 	state_ = state;
177 	QString prompt = state->getPrompt();
178 	QString def;
179 	if (state_->getFlags() & MCMDSTATE_UNPARSED) {
180 		if (preset.size() == 1) def = preset.at(0);
181 	} else {
182 		def = serializeCommand(preset);
183 	}
184 	uiSite_->mCmdReady(prompt, def);
185 	return true;
186 }
187 
188 
completeCommand(QString & command,int pos,int & start,int & end)189 QStringList MCmdManager::completeCommand(QString &command, int pos, int &start, int &end) {
190 	int part;
191 	QString query;
192 	char quotedAtPos;
193 	QStringList all;
194 	if (state_->getFlags() & MCMDSTATE_UNPARSED) {
195 		all << command;
196 		query = command.left(pos);
197 		part = -1;
198 	} else {
199 		all = parseCommand(command, pos, part, query, start, end, quotedAtPos);
200 	}
201 
202 	QStringList res;
203 	foreach(MCmdProviderIface *prov, providers_) {
204 		res += prov->mCmdTryCompleteCommand(state_, query, all, part);
205 	}
206 	res.sort();
207 
208 	QStringList quoted;
209 	if ((state_->getFlags() & MCMDSTATE_UNPARSED) == 0) {
210 		foreach(QString str, res) {
211 			QString trail;
212 			if (str.size() > 1 && str.at(str.size()-1) == QChar(0)) {
213 				str.chop(1);
214 				trail = " ";
215 			}
216 			str = str.replace("\\", "\\\\");
217 			if (quotedAtPos == 0) {
218 				quoted << str.replace(" ", "\\ ").replace("\"", "\\\"").replace("'", "\\'") + trail;
219 			} else {
220 				quoted << quotedAtPos + str.replace(quotedAtPos, QString("\\") + quotedAtPos) + trail;
221 			}
222 		}
223 	} else {
224 		quoted = res;
225 	}
226 	return quoted;
227 }
228 
isActive()229 bool MCmdManager::isActive() {
230 	return state_ != 0;
231 }
232 
233 
234 
registerProvider(MCmdProviderIface * prov)235 void MCmdManager::registerProvider(MCmdProviderIface *prov)
236 {
237 	if (! providers_.contains(prov)) {
238 		providers_ += prov;
239 	}
240 }
241