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