1 ////////////////////////////////////////////////////////////////////////////////
2 //    Scorched3D (c) 2000-2011
3 //
4 //    This file is part of Scorched3D.
5 //
6 //    Scorched3D is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Scorched3D is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License along
17 //    with this program; if not, write to the Free Software Foundation, Inc.,
18 //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include <console/ConsoleRules.h>
22 #include <console/Console.h>
23 #include <common/Defines.h>
24 
ConsoleRules()25 ConsoleRules::ConsoleRules()
26 {
27 
28 }
29 
~ConsoleRules()30 ConsoleRules::~ConsoleRules()
31 {
32 
33 }
34 
addRule(ConsoleRule * rule)35 void ConsoleRules::addRule(ConsoleRule *rule)
36 {
37 	removeRule(rule);
38 
39 	std::string addName = rule->getName();
40 	_strlwr((char *) addName.c_str());
41 	rules_.insert(std::pair<std::string, ConsoleRule *>(addName, rule));
42 }
43 
removeRule(ConsoleRule * rule)44 void ConsoleRules::removeRule(ConsoleRule *rule)
45 {
46 	std::multimap<std::string, ConsoleRule *>::iterator itor;
47 	for (itor = rules_.begin();
48 		itor != rules_.end();
49 		++itor)
50 	{
51 		ConsoleRule *r = itor->second;
52 		if (r == rule)
53 		{
54 			rules_.erase(itor);
55 			break;
56 		}
57 	}
58 }
59 
matchRule(const char * line,std::vector<ConsoleRule * > & matches)60 std::string ConsoleRules::matchRule(const char *line,
61 	std::vector<ConsoleRule *> &matches)
62 {
63 	std::vector<ConsoleRuleValue> values;
64 	parseLine(line, values);
65 
66 	if (values.empty())
67 	{
68 		std::multimap<std::string, ConsoleRule *>::iterator itor;
69 		for (itor = rules_.begin();
70 			itor != rules_.end();
71 			++itor)
72 		{
73 			ConsoleRule *rule = itor->second;
74 			matches.push_back(rule);
75 		}
76 		return "";
77 	}
78 	else
79 	{
80 		std::multimap<std::string, ConsoleRule *>::iterator itor;
81 		for (itor = rules_.begin();
82 			itor != rules_.end();
83 			++itor)
84 		{
85 			ConsoleRule *rule = itor->second;
86 			ConsoleRuleValue &nameValue = values[0];
87 
88 			if (values.size() == 1)
89 			{
90 				size_t nameLen = strlen(rule->getName());
91 				if (nameLen >= nameValue.valueString.length() &&
92 					_strnicmp(line, rule->getName(), nameValue.valueString.length()) == 0)
93 				{
94 					matches.push_back((*itor).second);
95 				}
96 			}
97 			else
98 			{
99 				if (0 == stricmp(rule->getName(), nameValue.valueString.c_str()))
100 				{
101 					if (rule->matchesPartialParams(values))
102 					{
103 						matches.push_back((*itor).second);
104 					}
105 				}
106 			}
107 		}
108 
109 		if (matches.empty()) return "";
110 		if (matches.size() == 1) return matches[0]->toString(values);
111 
112 		ConsoleRuleValue &firstValue = values[0];
113 		for (int i=(int) firstValue.valueString.length();; i++)
114 		{
115 			ConsoleRule *firstRule = matches[0];
116 			for (int j=0; j<(int)matches.size(); j++)
117 			{
118 				ConsoleRule *secondRule = matches[j];
119 				std::string firstString = firstRule->toString(values);
120 				std::string secondString = secondRule->toString(values);
121 
122 				if ((int) strlen(secondString.c_str()) < i ||
123 					0 != _strnicmp(secondString.c_str(), firstString.c_str(), i))
124 				{
125 					std::string buffer;
126 					buffer.append(secondString.c_str(), i - 1);
127 					return buffer;
128 				}
129 			}
130 		}
131 	}
132 
133 	return "";
134 }
135 
addLine(Console * console,const char * line)136 void ConsoleRules::addLine(Console *console, const char *line)
137 {
138 	std::vector<ConsoleRuleValue> values;
139 	if (!parseLine(line, values))
140 	{
141 		console->addLine(false, S3D::formatStringBuffer(
142 			"Non terminated quote in : %s",
143 			line));
144 		return;
145 	}
146 
147 	if (values.empty()) return; // Should never happen!
148 
149 	std::vector<ConsoleRule *> closeMatches;
150 	ConsoleRule *exactMatch = matchRule(values, closeMatches);
151 	if (exactMatch)
152 	{
153 		exactMatch->runRule(console, line, values);
154 	}
155 	else
156 	{
157 		std::string valuesString = ConsoleRule::valuesToString(values);
158 		console->addLine(false, "Unrecognised function :");
159 		console->addLine(false, S3D::formatStringBuffer(
160 			"  %s",
161 			valuesString.c_str()));
162 		if (!closeMatches.empty())
163 		{
164 			console->addLine(false, "Possible matches are :");
165 			std::vector<ConsoleRule *>::iterator itor;
166 			for (itor = closeMatches.begin();
167 				itor != closeMatches.end();
168 				++itor)
169 			{
170 				std::string text = (*itor)->toString();
171 				console->addLine(false, S3D::formatStringBuffer(
172 					"  %s", text.c_str()));
173 			}
174 		}
175 	}
176 }
177 
matchRule(std::vector<ConsoleRuleValue> & values,std::vector<ConsoleRule * > & closeMatches)178 ConsoleRule *ConsoleRules::matchRule(
179 	std::vector<ConsoleRuleValue> &values,
180 	std::vector<ConsoleRule *> &closeMatches)
181 {
182 	std::multimap<int, ConsoleRule *> matchedRules;
183 
184 	ConsoleRuleValue firstValue = values.front();
185 	_strlwr((char *)firstValue.valueString.c_str());
186 	std::pair<RulesMap::iterator, RulesMap::iterator> itp =
187 		rules_.equal_range(firstValue.valueString);
188 	for (RulesMap::iterator itor = itp.first; itor != itp.second; ++itor)
189 	{
190 		ConsoleRule *rule = itor->second;
191 		matchedRules.insert(
192 			std::pair<int, ConsoleRule *>(
193 				(int) rule->getParams().size(), rule));
194 	}
195 
196 	if (matchedRules.empty()) return 0;
197 
198 	std::vector<ConsoleRule *>::iterator ruleItor;
199 	std::vector<ConsoleRule *> sameNumberArgs;
200 	getMatchedRules(sameNumberArgs, matchedRules, (int) values.size() - 1);
201 	if (!sameNumberArgs.empty())
202 	{
203 		for (ruleItor = sameNumberArgs.begin();
204 			ruleItor != sameNumberArgs.end();
205 			++ruleItor)
206 		{
207 			ConsoleRule *rule = *ruleItor;
208 			closeMatches.push_back(rule);
209 			if (rule->matchesExactParams(values))
210 			{
211 				closeMatches.clear();
212 				return rule;
213 			}
214 		}
215 	}
216 
217 	return 0;
218 }
219 
getMatchedRules(std::vector<ConsoleRule * > & result,std::multimap<int,ConsoleRule * > & matchedRules,int argCount)220 void ConsoleRules::getMatchedRules(
221 	std::vector<ConsoleRule *> &result,
222 	std::multimap<int, ConsoleRule *> &matchedRules,
223 	int argCount)
224 {
225 	result.clear();
226 	std::pair<
227 		std::multimap<int, ConsoleRule *>::iterator,
228 		std::multimap<int, ConsoleRule *>::iterator> itp =
229 		matchedRules.equal_range(argCount);
230 	for (std::multimap<int, ConsoleRule *>::iterator itor = itp.first;
231 		itor != itp.second;
232 		++itor)
233 	{
234 		ConsoleRule *rule = itor->second;
235 		result.push_back(rule);
236 	}
237 }
238 
parseLine(const char * line,std::vector<ConsoleRuleValue> & split)239 bool ConsoleRules::parseLine(const char *line,
240 	std::vector<ConsoleRuleValue> &split)
241 {
242 	int pos = -1;
243 	bool inQuote = false;
244 	std::string currentEntry;
245 	for (int i=0; i<(int) strlen(line)+1; i++)
246 	{
247 		const char c = line[i];
248 
249 		if (c == '\0')
250 		{
251 			if (inQuote) return false;
252 			parseAddLine(pos, currentEntry.c_str(), split);
253 			currentEntry = ""; pos = -1;
254 
255 			return true;
256 		}
257 		else if (c == '=' &&
258 			((split.size() == 1 &&  currentEntry.empty()) ||
259 			 (split.size() == 0 && !currentEntry.empty())))
260 		{
261 			parseAddLine(pos, currentEntry.c_str(), split);
262 			currentEntry = ""; pos = -1;
263 		}
264 		else if ((c == ' ') && !inQuote)
265 		{
266 			parseAddLine(pos, currentEntry.c_str(), split);
267 			currentEntry = ""; pos = -1;
268 		}
269 		else if (c == '\"')
270 		{
271 			inQuote = !inQuote;
272 		}
273 		else
274 		{
275 			if (pos == -1) pos = i;
276 			currentEntry += c;
277 		}
278 	}
279 
280 	return !inQuote;
281 }
282 
parseAddLine(int position,const char * line,std::vector<ConsoleRuleValue> & split)283 void ConsoleRules::parseAddLine(int position, const char *line,
284 	std::vector<ConsoleRuleValue> &split)
285 {
286 	int n = (int) strlen(line);
287 	if (n == 0) return;
288 
289 	ConsoleRuleValue newSplit;
290 	newSplit.valueString = line;
291 	newSplit.position = position;
292 
293 	if (strcmp(line, "on") == 0)
294 	{
295 		newSplit.type = ConsoleRuleTypeBoolean;
296 		newSplit.valueBool = true;
297 	}
298 	else if (strcmp(line, "off") == 0)
299 	{
300 		newSplit.type = ConsoleRuleTypeBoolean;
301 		newSplit.valueBool = false;
302 	}
303 	else
304 	{
305 		bool numbersOnly = true;
306 		for (int i=0; i<n; i++)
307 		{
308 			if ((line[i] < '0') || (line[i] > '9'))
309 			{
310 				if (line[i]!='.')
311 				{
312 					numbersOnly = false;
313 					break;
314 				}
315 			}
316 		}
317 
318 		if (numbersOnly)
319 		{
320 			newSplit.type = ConsoleRuleTypeNumber;
321 			newSplit.valueNumber = (float) atof(line);
322 		}
323 		else
324 		{
325 			newSplit.type = ConsoleRuleTypeString;
326 		}
327 	}
328 
329 	split.push_back(newSplit);
330 }
331 
dump(std::vector<std::string> & resultList)332 void ConsoleRules::dump(std::vector<std::string> &resultList)
333 {
334 	std::multimap<std::string, ConsoleRule *>::iterator itor;
335 	for (itor = rules_.begin();
336 		itor != rules_.end();
337 		++itor)
338 	{
339 		ConsoleRule *rule = itor->second;
340 		resultList.push_back(rule->toString());
341 	}
342 }
343