1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include <algorithm>
4 #include <cctype>
5 #include <limits.h>
6 #include <stdexcept>
7 #include <sstream>
8 
9 #include <boost/scoped_array.hpp>
10 
11 #include "tdf_grammar.h"
12 #include "System/TdfParser.h"
13 #include "System/Util.h"
14 #include "System/FileSystem/FileHandler.h"
15 #include "System/Log/ILog.h"
16 
parse_error(size_t l,size_t c,std::string const & f)17 TdfParser::parse_error::parse_error(size_t l, size_t c, std::string const& f) throw()
18 	: content_error("Parse error in " + f + " at line " + IntToString(l) + " column " + IntToString(c) + ".")
19 	, line(l)
20 	, column(c)
21 	, filename(f)
22 {}
23 
parse_error(std::string const & line_of_error,size_t l,size_t c,std::string const & f)24 TdfParser::parse_error::parse_error(std::string const& line_of_error, size_t l, size_t c, std::string const& f) throw()
25 	: content_error("Parse error in " + f + " at line " + IntToString(l) + " column " + IntToString(c) +" near\n"+ line_of_error)
26 	, line(l)
27 	, column(c)
28 	, filename(f)
29 {}
30 
parse_error(std::string const & message,std::string const & line_of_error,size_t l,size_t c,std::string const & f)31 TdfParser::parse_error::parse_error(std::string const& message, std::string const& line_of_error, size_t l, size_t c, std::string const& f)
32 	throw()
33 	: content_error("Parse error '" + message + "' in " + f + " at line " + IntToString(l) + " column " + IntToString(c) +" near\n"+ line_of_error)
34 	, line(l)
35 	, column(c)
36 	, filename(f)
37 {}
38 
~parse_error()39 TdfParser::parse_error::~parse_error() throw() {}
get_line() const40 size_t TdfParser::parse_error::get_line() const { return line; }
get_column() const41 size_t TdfParser::parse_error::get_column() const { return column; }
get_filename() const42 std::string const& TdfParser::parse_error::get_filename() const { return filename; }
43 
print(std::ostream & out) const44 void TdfParser::TdfSection::print(std::ostream & out) const
45 {
46 	for (std::map<std::string,TdfSection*>::const_iterator it = sections.begin(), e=sections.end(); it != e; ++it) {
47 		out << "[" << it->first << "]\n{\n";
48 		it->second->print(out);
49 		out << "}\n";
50 	}
51 	for (std::map<std::string,std::string>::const_iterator it = values.begin(), e=values.end(); it != e; ++it) {
52 		out << it->first << "=" << it->second << ";\n";
53 	}
54 }
55 
construct_subsection(const std::string & name)56 TdfParser::TdfSection* TdfParser::TdfSection::construct_subsection(const std::string& name)
57 {
58 	std::string lowerd_name = StringToLower(name);
59 	std::map<std::string,TdfSection*>::iterator it = sections.find(lowerd_name);
60 	if (it != sections.end()) {
61 		return it->second;
62 	} else {
63 		TdfSection* ret = new TdfSection;
64 		sections[lowerd_name] = ret;
65 		return ret;
66 	}
67 }
68 
remove(const std::string & key,bool caseSensitive)69 bool TdfParser::TdfSection::remove(const std::string& key, bool caseSensitive)
70 {
71 	bool ret = false;
72 
73 	if (caseSensitive) {
74 		valueMap_t::iterator it = values.find(key);
75 		if ((ret = (it != values.end()))) {
76 			values.erase(it);
77 		}
78 	} else {
79 		// assume <key> is already in lowercase
80 		for (valueMap_t::iterator it = values.begin(); it != values.end(); ) {
81 			if (StringToLower(it->first) == key) {
82 				it = set_erase(values, it);
83 				ret = true;
84 			} else {
85 				++it;
86 			}
87 		}
88 	}
89 
90 	return ret;
91 }
92 
add_name_value(const std::string & name,const std::string & value)93 void TdfParser::TdfSection::add_name_value(const std::string& name, const std::string& value)
94 {
95 	std::string lowerd_name = StringToLower(name);
96 	values[lowerd_name] = value;
97 }
98 
~TdfSection()99 TdfParser::TdfSection::~TdfSection()
100 {
101 	for (std::map<std::string,TdfSection*>::iterator it = sections.begin(), e=sections.end(); it != e; ++it) {
102 		delete it->second;
103 	}
104 }
105 
106 
107 
TdfParser(char const * buf,size_t size)108 TdfParser::TdfParser(char const* buf, size_t size)
109 {
110 	LoadBuffer(buf, size);
111 }
112 
TdfParser(std::string const & filename)113 TdfParser::TdfParser(std::string const& filename)
114 {
115 	LoadFile(filename);
116 }
117 
~TdfParser()118 TdfParser::~TdfParser()
119 {
120 }
121 
print(std::ostream & out) const122 void TdfParser::print(std::ostream & out) const {
123 	root_section.print(out);
124 }
125 
126 
parse_buffer(char const * buf,size_t size)127 void TdfParser::parse_buffer(char const* buf, size_t size) {
128 
129 	std::list<std::string> junk_data;
130 	tdf_grammar grammar(&root_section, &junk_data);
131 	parse_info<char const*> info;
132 	std::string message;
133 	typedef position_iterator2<char const*> iterator_t;
134 	iterator_t error_it(buf, buf + size);
135 
136 	try {
137 		info = parse(
138 			buf
139 			, buf + size
140 			, grammar
141 			, space_p
142 				| comment_p("/*", "*/") // rule for C-comments
143 				| comment_p("//")
144 			);
145 	} catch (const parser_error<tdf_grammar::Errors, char const*>& ex) { // thrown by assertion parsers in tdf_grammar
146 
147 		switch(ex.descriptor) {
148 			case tdf_grammar::semicolon_expected: message = "semicolon expected"; break;
149 			case tdf_grammar::equals_sign_expected: message = "equals sign in name value pair expected"; break;
150 			case tdf_grammar::square_bracket_expected: message = "square bracket to close section name expected"; break;
151 			case tdf_grammar::brace_expected: message = "brace or further name value pairs expected"; break;
152 			default: message = "unknown boost::spirit::parser_error exception"; break;
153 		};
154 
155 		std::ptrdiff_t target_pos = ex.where - buf;
156 		for (int i = 1; i < target_pos; ++i) {
157 			++error_it;
158 			if (error_it != (iterator_t(buf + i, buf + size))) {
159 				++i;
160 			}
161 		}
162 	}
163 
164 	for (std::list<std::string>::const_iterator it = junk_data.begin(), e = junk_data.end(); it !=e ; ++it) {
165 		std::string temp = StringTrim(*it);
166 		if (!temp.empty()) {
167 			LOG_L(L_WARNING, "TdfParser: Junk in %s: %s",
168 					filename.c_str(), temp.c_str());
169 		}
170 	}
171 
172 	if (!message.empty()) {
173 		throw parse_error(message, error_it.get_currentline(), error_it.get_position().line, error_it.get_position().column, filename);
174 	}
175 
176 	// a different error might have happened:
177 	if (!info.full) {
178 		std::ptrdiff_t target_pos = info.stop - buf;
179 		for (int i = 1; i < target_pos; ++i) {
180 			++error_it;
181 			if (error_it != (iterator_t(buf + i, buf + size))) {
182 				++i;
183 			}
184 		}
185 
186 		throw parse_error(error_it.get_currentline(), error_it.get_position().line, error_it.get_position().column, filename);
187 	}
188 }
189 
LoadBuffer(char const * buf,size_t size)190 void TdfParser::LoadBuffer(char const* buf, size_t size)
191 {
192 	this->filename = "buffer";
193 	parse_buffer(buf, size);
194 }
195 
196 
LoadFile(std::string const & filename)197 void TdfParser::LoadFile(std::string const& filename)
198 {
199 
200 	this->filename = filename;
201 	CFileHandler file(filename);
202 	if (!file.FileExists()) {
203 		throw content_error("file " + filename + " not found");
204 	}
205 
206 	const size_t fileBuf_size = file.FileSize();
207 	//char* fileBuf = new char[fileBuf_size];
208 	boost::scoped_array<char> fileBuf(new char[fileBuf_size]);
209 
210 	file.Read(fileBuf.get(), file.FileSize());
211 	parse_buffer(fileBuf.get(), fileBuf_size);
212 
213 	//delete[] fileBuf;
214 }
215 
216 
SGetValueDef(std::string const & defaultValue,std::string const & location) const217 std::string TdfParser::SGetValueDef(std::string const& defaultValue, std::string const& location) const
218 {
219 	std::string lowerd = StringToLower(location);
220 	std::string value;
221 	bool found = SGetValue(value, lowerd);
222 	if (!found) {
223 		value = defaultValue;
224 	}
225 	return value;
226 }
227 
SGetValue(std::string & value,std::string const & location) const228 bool TdfParser::SGetValue(std::string &value, std::string const& location) const
229 {
230 	std::string lowerd = StringToLower(location);
231 	std::string searchpath; // for error-messages
232 	// split the location string
233 	const std::vector<std::string>& loclist = GetLocationVector(lowerd);
234 	sectionsMap_t::const_iterator sit = root_section.sections.find(loclist[0]);
235 	if (sit == root_section.sections.end()) {
236 		value = "Section " + loclist[0] + " missing in file " + filename;
237 		return false;
238 	}
239 	TdfSection* sectionptr = sit->second;
240 	searchpath = loclist[0];
241 	for (unsigned int i=1; i < loclist.size()-1; ++i) {
242 		//const char *arg = loclist[i].c_str();
243 		searchpath += '\\';
244 		searchpath += loclist[i];
245 		sit = sectionptr->sections.find(loclist[i]);
246 		if (sit == sectionptr->sections.end()) {
247 			value = "Section " + searchpath + " missing in file " + filename;
248 			return false;
249 		}
250 		sectionptr = sit->second;
251 	}
252 	searchpath += '\\';
253 	searchpath += loclist[loclist.size()-1];
254 
255 	valueMap_t::const_iterator vit =
256 		sectionptr->values.find(loclist[loclist.size()-1]);
257 	if (vit == sectionptr->values.end()) {
258 		value = "Value " + searchpath + " missing in file " + filename;
259 		return false;
260 	}
261 	std::string svalue = vit->second;
262 	value = svalue;
263 	return true;
264 }
265 
GetValue(bool & val,const std::string & location) const266 bool TdfParser::GetValue(bool& val, const std::string& location) const
267 {
268 	std::string buf;
269 	if (SGetValue(buf, location)) {
270 		int tempval;
271 		std::istringstream stream(buf);
272 		stream >> tempval;
273 		if (tempval == 0) {
274 			val = false;
275 		} else {
276 			val = true;
277 		}
278 		return true;
279 	} else {
280 		return false;
281 	}
282 }
283 
GetAllValues(std::string const & location) const284 const TdfParser::valueMap_t& TdfParser::GetAllValues(std::string const& location) const
285 {
286 	static valueMap_t emptymap;
287 	std::string lowerd = StringToLower(location);
288 	const std::vector<std::string>& loclist = GetLocationVector(lowerd);
289 	sectionsMap_t::const_iterator sit = root_section.sections.find(loclist[0]);
290 	if (sit == root_section.sections.end()) {
291 		LOG_L(L_WARNING, "Section %s missing in file %s",
292 				loclist[0].c_str(), filename.c_str());
293 		return emptymap;
294 	}
295 	TdfSection* sectionptr = sit->second;
296 	std::string searchpath = loclist[0]; // for error-messages
297 	for (unsigned int i=1; i < loclist.size(); i++) {
298 		searchpath += '\\';
299 		searchpath += loclist[i];
300 		sit = sectionptr->sections.find(loclist[i]);
301 		if (sit == sectionptr->sections.end()) {
302 			LOG_L(L_WARNING, "Section %s missing in file %s",
303 					searchpath.c_str(), filename.c_str());
304 			return emptymap;
305 		}
306 		sectionptr = sit->second;
307 	}
308 	return sectionptr->values;
309 }
310 
GetSectionList(std::string const & location) const311 std::vector<std::string> TdfParser::GetSectionList(std::string const& location) const
312 {
313 	std::string lowerd = StringToLower(location);
314 	const std::vector<std::string>& loclist = GetLocationVector(lowerd);
315 	std::vector<std::string> returnvec;
316 	const sectionsMap_t* sectionsptr = &root_section.sections;
317 	if (!loclist[0].empty()) {
318 		std::string searchpath;
319 		for (unsigned int i = 0; i < loclist.size(); i++) {
320 			searchpath += loclist[i];
321 			if (sectionsptr->find(loclist[i]) == sectionsptr->end()) {
322 				LOG_L(L_WARNING, "Section %s missing in file %s",
323 						searchpath.c_str(), filename.c_str());
324 				return returnvec;
325 			}
326 			sectionsptr = &sectionsptr->find(loclist[i])->second->sections;
327 			searchpath += '\\';
328 		}
329 	}
330 	std::map<std::string,TdfSection*>::const_iterator it;
331 	for (it = sectionsptr->begin(); it != sectionsptr->end(); ++it) {
332 		returnvec.push_back(it->first);
333 		StringToLowerInPlace(returnvec.back());
334 	}
335 	return returnvec;
336 }
337 
SectionExist(std::string const & location) const338 bool TdfParser::SectionExist(std::string const& location) const
339 {
340 	std::string lowerd = StringToLower(location);
341 	const std::vector<std::string>& loclist = GetLocationVector(lowerd);
342 	sectionsMap_t::const_iterator sit = root_section.sections.find(loclist[0]);
343 	if (sit == root_section.sections.end()) {
344 		return false;
345 	}
346 	TdfSection* sectionptr = sit->second;
347 	for (unsigned int i = 1; i < loclist.size(); i++) {
348 		sit = sectionptr->sections.find(loclist[i]);
349 		if (sit == sectionptr->sections.end()) {
350 			return false;
351 		}
352 		sectionptr = sectionptr->sections[loclist[i]];
353 	}
354 	return true;
355 }
356 
GetLocationVector(std::string const & location) const357 std::vector<std::string> TdfParser::GetLocationVector(std::string const& location) const
358 {
359 	std::string lowerd = StringToLower(location);
360 	std::vector<std::string> loclist;
361 	std::string::size_type start = 0;
362 	std::string::size_type next = 0;
363 
364 	while ((next = lowerd.find_first_of("\\", start)) != std::string::npos) {
365 		loclist.push_back(lowerd.substr(start, next-start));
366 		start = next + 1;
367 	}
368 	loclist.push_back(lowerd.substr(start));
369 
370 	return loclist;
371 }
372 
GetFloat3(float3 def,std::string const & location) const373 float3 TdfParser::GetFloat3(float3 def, std::string const& location) const
374 {
375 	std::string s = SGetValueDef("", location);
376 	if (s.empty()) {
377 		return def;
378 	}
379 	float3 ret;
380 	ParseArray(s, &ret.x, 3);
381 	return ret;
382 }
383