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 = §ionsptr->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