1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/xmlparser.h"
24 #include "common/archive.h"
25 #include "common/fs.h"
26 #include "common/memstream.h"
27 #include "common/system.h"
28 
29 namespace Common {
30 
~XMLParser()31 XMLParser::~XMLParser() {
32 	while (!_activeKey.empty())
33 		freeNode(_activeKey.pop());
34 
35 	delete _XMLkeys;
36 	delete _stream;
37 
38 	for (List<XMLKeyLayout *>::iterator i = _layoutList.begin();
39 		i != _layoutList.end(); ++i)
40 		delete *i;
41 
42 	_layoutList.clear();
43 }
44 
loadFile(const String & filename)45 bool XMLParser::loadFile(const String &filename) {
46 	_stream = SearchMan.createReadStreamForMember(filename);
47 	if (!_stream)
48 		return false;
49 
50 	_fileName = filename;
51 	return true;
52 }
53 
loadFile(const FSNode & node)54 bool XMLParser::loadFile(const FSNode &node) {
55 	_stream = node.createReadStream();
56 	if (!_stream)
57 		return false;
58 
59 	_fileName = node.getName();
60 	return true;
61 }
62 
loadBuffer(const byte * buffer,uint32 size,DisposeAfterUse::Flag disposable)63 bool XMLParser::loadBuffer(const byte *buffer, uint32 size, DisposeAfterUse::Flag disposable) {
64 	_stream = new MemoryReadStream(buffer, size, disposable);
65 	_fileName = "Memory Stream";
66 	return true;
67 }
68 
loadStream(SeekableReadStream * stream)69 bool XMLParser::loadStream(SeekableReadStream *stream) {
70 	_stream = stream;
71 	_fileName = "File Stream";
72 	return _stream != nullptr;
73 }
74 
close()75 void XMLParser::close() {
76 	delete _stream;
77 	_stream = nullptr;
78 }
79 
parserError(const String & errStr)80 bool XMLParser::parserError(const String &errStr) {
81 	_state = kParserError;
82 
83 	const int startPosition = _stream->pos();
84 	int currentPosition = startPosition;
85 	int lineCount = 1;
86 	char c = 0;
87 
88 	_stream->seek(0, SEEK_SET);
89 
90 	while (currentPosition--) {
91 		c = _stream->readByte();
92 
93 		if (c == '\n' || c == '\r')
94 			lineCount++;
95 	}
96 
97 	assert(_stream->pos() == startPosition);
98 	currentPosition = startPosition;
99 
100 	Common::String errorMessage = Common::String::format("\n  File <%s>, line %d:\n", _fileName.c_str(), lineCount);
101 
102 	if (startPosition > 1) {
103 		int keyOpening = 0;
104 		int keyClosing = 0;
105 
106 		while (currentPosition-- && keyOpening == 0) {
107 			_stream->seek(-2, SEEK_CUR);
108 			c = _stream->readByte();
109 
110 			if (c == '<')
111 				keyOpening = currentPosition - 1;
112 			else if (c == '>')
113 				keyClosing = currentPosition;
114 		}
115 
116 		_stream->seek(startPosition, SEEK_SET);
117 		currentPosition = startPosition;
118 		while (keyClosing == 0 && c && currentPosition++) {
119 			c = _stream->readByte();
120 
121 			if (c == '>')
122 				keyClosing = currentPosition;
123 		}
124 
125 		currentPosition = (keyClosing - keyOpening);
126 		_stream->seek(keyOpening, SEEK_SET);
127 
128 		while (currentPosition--)
129 			errorMessage += (char)_stream->readByte();
130 	}
131 
132 	errorMessage += "\n\nParser error: ";
133 	errorMessage += errStr;
134 	errorMessage += "\n\n";
135 
136 	g_system->logMessage(LogMessageType::kError, errorMessage.c_str());
137 
138 	return false;
139 }
140 
parseXMLHeader(ParserNode * node)141 bool XMLParser::parseXMLHeader(ParserNode *node) {
142 	assert(node->header);
143 
144 	if (_activeKey.size() != 1)
145 		return parserError("XML Header is expected in the global scope.");
146 
147 	if (!node->values.contains("version"))
148 		return parserError("Missing XML version in XML header.");
149 
150 	if (node->values["version"] != "1.0")
151 		return parserError("Unsupported XML version.");
152 
153 	return true;
154 }
155 
parseActiveKey(bool closed)156 bool XMLParser::parseActiveKey(bool closed) {
157 	bool ignore = false;
158 	assert(_activeKey.empty() == false);
159 
160 	ParserNode *key = _activeKey.top();
161 
162 	if (key->name == "xml" && key->header == true) {
163 		assert(closed);
164 		return parseXMLHeader(key) && closeKey();
165 	}
166 
167 	XMLKeyLayout *layout = (_activeKey.size() == 1) ? _XMLkeys : getParentNode(key)->layout;
168 
169 	if (layout->children.contains(key->name)) {
170 		key->layout = layout->children[key->name];
171 
172 		const StringMap &localMap = key->values;
173 		int keyCount = localMap.size();
174 
175 		for (List<XMLKeyLayout::XMLKeyProperty>::const_iterator i = key->layout->properties.begin(); i != key->layout->properties.end(); ++i) {
176 			if (i->required && !localMap.contains(i->name))
177 				return parserError("Missing required property '" + i->name + "' inside key '" + key->name + "'");
178 			else if (localMap.contains(i->name))
179 				keyCount--;
180 		}
181 
182 		if (keyCount > 0)
183 			return parserError("Unhandled property inside key '" + key->name + "'.");
184 
185 	} else {
186 		return parserError("Unexpected key in the active scope ('" + key->name + "').");
187 	}
188 
189 	// check if any of the parents must be ignored.
190 	// if a parent is ignored, all children are too.
191 	for (int i = _activeKey.size() - 1; i >= 0; --i) {
192 		if (_activeKey[i]->ignore)
193 			ignore = true;
194 	}
195 
196 	if (ignore == false && keyCallback(key) == false) {
197 		// HACK:  People may be stupid and overlook the fact that
198 		// when keyCallback() fails, a parserError() must be set.
199 		// We set it manually in that case.
200 		if (_state != kParserError)
201 			parserError("Unhandled exception when parsing '" + key->name + "' key.");
202 
203 		return false;
204 	}
205 
206 	if (closed)
207 		return closeKey();
208 
209 	return true;
210 }
211 
parseKeyValue(String keyName)212 bool XMLParser::parseKeyValue(String keyName) {
213 	assert(_activeKey.empty() == false);
214 
215 	if (_activeKey.top()->values.contains(keyName))
216 		return false;
217 
218 	_token.clear();
219 	char stringStart;
220 
221 	if (_char == '"' || _char == '\'') {
222 		stringStart = _char;
223 		_char = _stream->readByte();
224 
225 		while (_char && _char != stringStart) {
226 			_token += _char;
227 			_char = _stream->readByte();
228 		}
229 
230 		if (_char == 0)
231 			return false;
232 
233 		_char = _stream->readByte();
234 
235 	} else if (!parseToken()) {
236 		return false;
237 	}
238 
239 	_activeKey.top()->values[keyName] = _token;
240 	return true;
241 }
242 
parseIntegerKey(const char * key,int count,...)243 bool XMLParser::parseIntegerKey(const char *key, int count, ...) {
244 	bool result;
245 	va_list args;
246 	va_start(args, count);
247 	result = vparseIntegerKey(key, count, args);
248 	va_end(args);
249 	return result;
250 }
251 
parseIntegerKey(const String & key,int count,...)252 bool XMLParser::parseIntegerKey(const String &key, int count, ...) {
253 	bool result;
254 	va_list args;
255 	va_start(args, count);
256 	result = vparseIntegerKey(key.c_str(), count, args);
257 	va_end(args);
258 	return result;
259 }
260 
vparseIntegerKey(const char * key,int count,va_list args)261 bool XMLParser::vparseIntegerKey(const char *key, int count, va_list args) {
262 	char *parseEnd;
263 	int *num_ptr;
264 
265 	while (count--) {
266 		while (isSpace(*key))
267 			key++;
268 
269 		num_ptr = va_arg(args, int*);
270 		*num_ptr = strtol(key, &parseEnd, 10);
271 
272 		key = parseEnd;
273 
274 		while (isSpace(*key))
275 			key++;
276 
277 		if (count && *key++ != ',')
278 			return false;
279 	}
280 
281 	return (*key == 0);
282 }
283 
closeKey()284 bool XMLParser::closeKey() {
285 	bool ignore = false;
286 	bool result = true;
287 
288 	for (int i = _activeKey.size() - 1; i >= 0; --i) {
289 		if (_activeKey[i]->ignore)
290 			ignore = true;
291 	}
292 
293 	if (ignore == false)
294 		result = closedKeyCallback(_activeKey.top());
295 
296 	freeNode(_activeKey.pop());
297 
298 	return result;
299 }
300 
parse()301 bool XMLParser::parse() {
302 	if (_stream == nullptr)
303 		return false;
304 
305 	// Make sure we are at the start of the stream.
306 	_stream->seek(0, SEEK_SET);
307 
308 	if (_XMLkeys == nullptr)
309 		buildLayout();
310 
311 	while (!_activeKey.empty())
312 		freeNode(_activeKey.pop());
313 
314 	cleanup();
315 
316 	bool activeClosure = false;
317 	bool activeHeader = false;
318 	bool selfClosure;
319 
320 	_state = kParserNeedHeader;
321 	_activeKey.clear();
322 
323 	_char = _stream->readByte();
324 
325 	while (_char && _state != kParserError) {
326 		if (skipSpaces())
327 			continue;
328 
329 		if (skipComments())
330 			continue;
331 
332 		switch (_state) {
333 		case kParserNeedHeader:
334 		case kParserNeedKey:
335 			if (_char != '<') {
336 				parserError("Parser expecting key start.");
337 				break;
338 			}
339 
340 			if ((_char = _stream->readByte()) == 0) {
341 				parserError("Unexpected end of file.");
342 				break;
343 			}
344 
345 			if (_state == kParserNeedHeader) {
346 				if (_char != '?') {
347 					parserError("Expecting XML header.");
348 					break;
349 				}
350 
351 				_char = _stream->readByte();
352 				activeHeader = true;
353 			} else if (_char == '/') {
354 				_char = _stream->readByte();
355 				activeClosure = true;
356 			} else if (_char == '?') {
357 				parserError("Unexpected header. There may only be one XML header per file.");
358 				break;
359 			}
360 
361 			_state = kParserNeedKeyName;
362 			break;
363 
364 		case kParserNeedKeyName:
365 			if (!parseToken()) {
366 				parserError("Invalid key name.");
367 				break;
368 			}
369 
370 			if (activeClosure) {
371 				if (_activeKey.empty() || _token != _activeKey.top()->name) {
372 					parserError("Unexpected closure.");
373 					break;
374 				}
375 			} else {
376 				ParserNode *node = allocNode(); // new ParserNode;
377 				node->name = _token;
378 				node->ignore = false;
379 				node->header = activeHeader;
380 				node->depth = _activeKey.size();
381 				node->layout = nullptr;
382 				_activeKey.push(node);
383 			}
384 
385 			_state = kParserNeedPropertyName;
386 			break;
387 
388 		case kParserNeedPropertyName:
389 			if (activeClosure) {
390 				if (!closeKey()) {
391 					parserError("Missing data when closing key '" + _activeKey.top()->name + "'.");
392 					break;
393 				}
394 
395 				activeClosure = false;
396 
397 				if (_char != '>')
398 					parserError("Invalid syntax in key closure.");
399 				else
400 					_state = kParserNeedKey;
401 
402 				_char = _stream->readByte();
403 				break;
404 			}
405 
406 			selfClosure = false;
407 
408 			if (_char == '/' || (_char == '?' && activeHeader)) {
409 				selfClosure = true;
410 				_char = _stream->readByte();
411 			}
412 
413 			if (_char == '>') {
414 				if (activeHeader && !selfClosure) {
415 					parserError("XML Header must be self-closed.");
416 				} else if (parseActiveKey(selfClosure)) {
417 					_char = _stream->readByte();
418 					_state = kParserNeedKey;
419 				}
420 
421 				activeHeader = false;
422 				break;
423 			}
424 
425 			if (selfClosure)
426 				parserError("Expecting key closure after '/' symbol.");
427 			else if (!parseToken())
428 				parserError("Error when parsing key value.");
429 			else
430 				_state = kParserNeedPropertyOperator;
431 
432 			break;
433 
434 		case kParserNeedPropertyOperator:
435 			if (_char != '=')
436 				parserError("Syntax error after key name.");
437 			else
438 				_state = kParserNeedPropertyValue;
439 
440 			_char = _stream->readByte();
441 			break;
442 
443 		case kParserNeedPropertyValue:
444 			if (!parseKeyValue(_token))
445 				parserError("Invalid key value.");
446 			else
447 				_state = kParserNeedPropertyName;
448 
449 			break;
450 
451 		default:
452 			break;
453 		}
454 	}
455 
456 	if (_state == kParserError)
457 		return false;
458 
459 	if (_state != kParserNeedKey || !_activeKey.empty())
460 		return parserError("Unexpected end of file.");
461 
462 	return true;
463 }
464 
skipSpaces()465 bool XMLParser::skipSpaces() {
466 	if (!isSpace(_char))
467 		return false;
468 
469 	while (_char && isSpace(_char))
470 		_char = _stream->readByte();
471 
472 	return true;
473 }
474 
skipComments()475 bool XMLParser::skipComments() {
476 	if (_char == '<') {
477 		_char = _stream->readByte();
478 
479 		if (_char != '!') {
480 			_stream->seek(-1, SEEK_CUR);
481 			_char = '<';
482 			return false;
483 		}
484 
485 		if (_stream->readByte() != '-' || _stream->readByte() != '-')
486 			return parserError("Malformed comment syntax.");
487 
488 		_char = _stream->readByte();
489 
490 		while (_char) {
491 			if (_char == '-') {
492 				if (_stream->readByte() == '-') {
493 
494 					if (_stream->readByte() != '>')
495 						return parserError("Malformed comment (double-hyphen inside comment body).");
496 
497 					_char = _stream->readByte();
498 					return true;
499 				}
500 			}
501 
502 			_char = _stream->readByte();
503 		}
504 
505 		return parserError("Comment has no closure.");
506 	}
507 
508 	return false;
509 }
510 
parseToken()511 bool XMLParser::parseToken() {
512 	_token.clear();
513 
514 	while (isValidNameChar(_char)) {
515 		_token += _char;
516 		_char = _stream->readByte();
517 	}
518 
519 	return isSpace(_char) != 0 || _char == '>' || _char == '=' || _char == '/';
520 }
521 
522 } // End of namespace Common
523