36 #include <boost/algorithm/string/replace.hpp>
37 #include <boost/iostreams/filtering_stream.hpp>
38 #include <boost/iostreams/filter/bzip2.hpp>
39 #include <boost/iostreams/filter/gzip.hpp>
40 #include <boost/variant/static_visitor.hpp>
43 #define ERR_CF LOG_STREAM(err, log_config)
44 #define WRN_CF LOG_STREAM(warn, log_config)
45 #define LOG_CF LOG_STREAM(info, log_config)
53 parser(
const parser&);
54 parser& operator=(
const parser&);
56 parser(
config& cfg, std::istream&
in,
63 void parse_variable();
77 cfg(cfg),
name(name), start_line(start_line), file(file)
86 std::stack<element> elements;
92 validator_(validator),
101 void parser::operator()()
104 elements.push(element(&cfg_,
""));
109 switch(tok_.current_token().type) {
119 if (static_cast<unsigned char>(tok_.current_token().value[0]) == 0xEF &&
120 static_cast<unsigned char>(tok_.next_token().value[0]) == 0xBB &&
121 static_cast<unsigned char>(tok_.next_token().value[0]) == 0xBF)
124 std::stringstream ss;
125 ss << tok_.get_start_line() <<
" " << tok_.get_file();
128 "Skipping over a utf8 BOM at $pos")
131 error(
_(
"Unexpected characters at line start"));
137 }
while (tok_.current_token().type !=
token::END);
140 assert(!elements.empty());
142 if(elements.size() != 1) {
144 i18n_symbols[
"tag"] = elements.top().name;
145 std::stringstream ss;
146 ss << elements.top().start_line <<
" " << elements.top().file;
148 _(
"Missing closing tag for tag [$tag]"),
149 _(
"expected at $pos")),
_(
"opened at $pos"));
153 void parser::parse_element()
157 config* current_element =
nullptr;
158 switch(tok_.current_token().type) {
160 elname = tok_.current_token().value;
161 if (tok_.next_token().type !=
']')
162 error(
_(
"Unterminated [element] tag"));
164 current_element = &(elements.top().cfg->add_child(elname));
165 elements.push(element(current_element, elname, tok_.get_start_line(), tok_.get_file()));
167 validator_->open_tag(elname,tok_.get_start_line(),
174 error(
_(
"Invalid tag name"));
175 elname = tok_.current_token().value;
176 if (tok_.next_token().type !=
']')
177 error(
_(
"Unterminated [+element] tag"));
181 if (
config &
c = elements.top().cfg->
child(elname, -1)) {
182 current_element = &
c;
184 validator_->open_tag(elname,tok_.get_start_line(),
185 tok_.get_file(),
true);
188 current_element = &elements.top().cfg->
add_child(elname);
190 validator_->open_tag(elname,tok_.get_start_line(),
194 elements.push(element(current_element, elname, tok_.get_start_line(), tok_.get_file()));
199 error(
_(
"Invalid closing tag name"));
200 elname = tok_.current_token().value;
201 if(tok_.next_token().type !=
']')
202 error(
_(
"Unterminated closing tag"));
203 if(elements.size() <= 1)
204 error(
_(
"Unexpected closing tag"));
205 if(elname != elements.top().name) {
207 i18n_symbols[
"tag1"] = elements.top().name;
208 i18n_symbols[
"tag2"] = elname;
209 std::stringstream ss;
210 ss << elements.top().start_line <<
" " << elements.top().file;
212 _(
"Found invalid closing tag [/$tag2] for tag [$tag1]"),
213 _(
"opened at $pos")),
_(
"closed at $pos"));
216 element & el= elements.top();
217 validator_->validate(*el.cfg,el.name,el.start_line,el.file);
218 validator_->close_tag();
223 error(
_(
"Invalid tag name"));
227 void parser::parse_variable()
229 config& cfg = *elements.top().cfg;
230 std::vector<std::string> variables;
231 variables.push_back(
"");
233 while (tok_.current_token().type !=
'=') {
234 switch(tok_.current_token().type) {
236 if(!variables.back().empty())
237 variables.back() +=
' ';
238 variables.back() += tok_.current_token().value;
241 if(variables.back().empty()) {
242 error(
_(
"Empty variable name"));
244 variables.push_back(
"");
248 error(
_(
"Unexpected characters after variable name (expected , or =)"));
253 if(variables.back().empty())
254 error(
_(
"Empty variable name"));
258 std::vector<std::string>::const_iterator curvar = variables.begin();
260 bool ignore_next_newlines =
false, previous_string =
false;
263 assert(curvar != variables.end());
265 switch (tok_.current_token().type) {
267 if ((curvar+1) != variables.end()) {
271 cfg[*curvar] = buffer.
value();
273 validator_->validate_key (cfg,*curvar,buffer.
value(),
274 tok_.get_start_line (),
285 switch (tok_.current_token().type) {
287 error(
_(
"Unterminated quoted string"));
290 buffer +=
t_string_base(tok_.current_token().value, tok_.textdomain());
294 buffer += tok_.current_token().
value;
303 ignore_next_newlines =
true;
306 if (previous_string) buffer +=
" ";
309 buffer += tok_.current_token().
value;
312 buffer += tok_.current_token().
value;
315 error(
_(
"Unterminated quoted string"));
318 if (ignore_next_newlines)
continue;
324 previous_string = tok_.current_token().type ==
token::STRING;
325 ignore_next_newlines =
false;
332 cfg[*curvar] = buffer.
value();
334 validator_->validate_key (cfg,*curvar,buffer.
value(),
335 tok_.get_start_line (),
338 while (++curvar != variables.end()) {
355 if(!hint_string.empty()) {
356 result +=
'\n' + hint_string;
359 if(!debug_string.empty()) {
360 result +=
'\n' + debug_string;
363 for(utils::string_map::value_type& var : i18n_symbols)
372 if(hint_string.empty()) {
373 hint_string =
_(
"at $pos");
377 i18n_symbols[
"error"] = error_type;
379 std::stringstream ss;
380 ss << tok_.get_start_line() <<
" " << tok_.get_file();
382 #ifdef DEBUG_TOKENIZER
383 i18n_symbols[
"value"] = tok_.current_token().value;
384 i18n_symbols[
"previous_value"] = tok_.previous_token().value;
387 _(
"Value: '$value' Previous: '$previous_value'");
393 lineno_string(i18n_symbols, ss.str(),
"$error", hint_string, tok_state);
402 parser(cfg, in, validator)();
407 std::istringstream ss(in);
408 parser(cfg, ss, validator)();
411 template <
typename decompressor>
416 if (file.peek() == EOF) {
419 boost::iostreams::filtering_stream<boost::iostreams::input>
filter;
420 filter.push(decompressor());
429 filter.exceptions(filter.exceptions() | std::ios_base::badbit);
440 if(filter.peek() == EOF) {
441 LOG_CF <<
"Empty compressed file or error at reading a compressed file.";
447 LOG_CF <<
" filter.peek() != EOF but !filter.good(), this indicates a malformed gz stream, and can make wesnoth crash.";
450 parser(cfg, filter,validator)();
456 read_compressed<boost::iostreams::gzip_decompressor>(cfg, file, validator);
462 read_compressed<boost::iostreams::bzip2_decompressor>(cfg, file, validator);
470 std::string escaped_string(
const std::string::const_iterator &begin,
471 const std::string::const_iterator &
end)
474 std::string::const_iterator iter = begin;
475 while ( iter != end ) {
476 const char c = *iter;
477 res.append(c ==
'"' ? 2 : 1, c);
488 return escaped_string(value.begin(), value.end());
491 class write_key_val_visitor :
public boost::static_visitor<void>
494 const unsigned level_;
499 write_key_val_visitor(std::ostream &out,
unsigned level,
501 : out_(out), level_(level), textdomain_(textdomain), key_(key)
505 template <
typename T>
void operator()(T
const &
v)
const
506 {
indent(); out_ << key_ <<
'=' << v <<
'\n'; }
509 void operator()(boost::blank
const &)
const
512 {
indent(); out_ << key_ <<
'=' <<
'"' << escaped_string(s) <<
'"' <<
'\n'; }
513 void operator()(
t_string const &
s)
const;
517 {
for (
unsigned i = 0;
i < level_; ++
i ) out_ <<
'\t'; }
527 void write_key_val_visitor::operator()(
t_string const &value)
const
536 if (
w.translatable() &&
w.textdomain() != textdomain_) {
537 textdomain_ =
w.textdomain();
538 out_ <<
"#textdomain " << textdomain_ <<
'\n';
548 if (
w.translatable())
551 out_ <<
'"' << escaped_string(
w.begin(),
w.end()) <<
'"';
562 value.
apply_visitor(write_key_val_visitor(out, level, textdomain, key));
567 out <<
std::string(level,
'\t') <<
'[' << child <<
"]\n";
572 out <<
std::string(level,
'\t') <<
"[/" << child <<
"]\n";
578 throw config::error(
"Too many recursion levels in config write");
582 ERR_CF <<
"Config contains invalid attribute name '" <<
i.first <<
"', skipping...\n";
591 ERR_CF <<
"Config contains invalid tag name '" << item.key <<
"', skipping...\n";
603 throw config::error(
"Too many recursion levels in config write");
608 for (
const auto &pair: cfg.
subtags_)
610 assert(pair.first && pair.second);
612 ERR_CF <<
"Config contains invalid tag name '" << *pair.first <<
"', skipping...\n";
627 template <
typename compressor>
630 boost::iostreams::filtering_stream<boost::iostreams::output>
filter;
631 filter.push(compressor());
641 write_compressed<boost::iostreams::gzip_compressor>(out, cfg);
646 write_compressed<boost::iostreams::bzip2_compressor>(out, cfg);
std::string lineno_string(const std::string &lineno)
void write_gz(std::ostream &out, configr_of const &cfg)
void write_compressed(std::ostream &out, configr_of const &cfg)
Abstract baseclass for the tokenizer.
bool translatable() const
static lg::log_domain log_config("config")
static l_noret error(LoadState *S, const char *why)
void write_key_val(std::ostream &out, const std::string &key, const config::attribute_value &value, unsigned level, std::string &textdomain)
void write_bz2(std::ostream &out, configr_of const &cfg)
attribute_map::value_type attribute
V::result_type apply_visitor(const V &visitor) const
Applies a visitor to the underlying variant.
This file contains information about validation abstract level interface.
void write_open_child(std::ostream &out, const std::string &child, unsigned int level)
Definitions for the interface to Wesnoth Markup Language (WML).
Variant for storing WML attributes.
void read_gz(config &cfg, std::istream &file, abstract_validator *validator)
might throw a std::ios_base::failure especially a gzip_error
void write_close_child(std::ostream &out, const std::string &child, unsigned int level)
static bool valid_id(const std::string &id)
Used in parsing config file.
static UNUSEDNOWARN std::string _(const char *str)
std::map< std::string, t_string > string_map
GLubyte GLubyte GLubyte GLubyte w
GLsizei const GLfloat * value
all_children_itors all_children_range() const
In-order iteration over all children.
config & add_child(const std::string &key)
void read_bz2(config &cfg, std::istream &file, abstract_validator *validator)
might throw a std::ios_base::failure especially bzip2_error
void read_compressed(config &cfg, std::istream &file, abstract_validator *validator)
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.
const_attr_itors attribute_range() const
static void write_internal(config const &cfg, std::ostream &out, std::string &textdomain, size_t tab=0)
void read(config &cfg, std::istream &in, abstract_validator *validator)
const std::string & value() const
GLuint const GLchar * name
std::vector< std::pair< const std::string *, const configr_of * > > subtags_
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
static const size_t max_recursion_levels
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Standard logging facilities (interface).
GLsizei GLenum GLuint GLuint GLsizei char * message
A config object defines a single node in a WML file, with access to child nodes.
void write(std::ostream &out, configr_of const &cfg, unsigned int level)
GLsizei const GLcharARB ** string