The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
preprocessor.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 by David White <[email protected]>
3  Copyright (C) 2005 - 2016 by Guillaume Melquiond <[email protected]>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
5 
6  This program 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  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * WML preprocessor.
19  */
20 
21 #include "global.hpp"
22 
23 #include "buffered_istream.hpp"
24 #include "config.hpp"
25 #include "filesystem.hpp"
26 #include "game_config.hpp"
27 #include "log.hpp"
28 #include "wesconfig.h"
31 #include "serialization/parser.hpp"
32 #include "filesystem.hpp"
33 #include "util.hpp"
34 #include "version.hpp"
35 
36 #include <stdexcept>
37 
38 static lg::log_domain log_preprocessor("preprocessor");
39 #define ERR_PREPROC LOG_STREAM(err, log_preprocessor)
40 #define WRN_PREPROC LOG_STREAM(warn, log_preprocessor)
41 #define LOG_PREPROC LOG_STREAM(info, log_preprocessor)
42 #define DBG_PREPROC LOG_STREAM(debug, log_preprocessor)
43 
44 using std::streambuf;
45 
46 static std::string current_file_str = "CURRENT_FILE";
47 static std::string current_dir_str = "CURRENT_DIRECTORY";
48 
49 // map associating each filename encountered to a number
50 typedef std::map<std::string, int> t_file_number_map;
52 
53 static bool encode_filename = true;
54 
56 
57 // get filename associated to this code
58 static std::string get_filename(const std::string& file_code){
59  if(!encode_filename)
60  return file_code;
61 
62  std::stringstream s;
63  s << file_code;
64  int n = 0;
65  s >> std::hex >> n;
66 
67  for(const t_file_number_map::value_type& p : file_number_map) {
68  if(p.second == n)
69  return p.first;
70  }
71  return "<unknown>";
72 }
73 
74 // get code associated to this filename
75 static std::string get_file_code(const std::string& filename){
76  if(!encode_filename)
77  return filename;
78 
79  // current number of encountered filenames
80  static int current_file_number = 0;
81 
82  int& fnum = file_number_map[utils::escape(filename, " \\")];
83  if(fnum == 0)
84  fnum = ++current_file_number;
85 
86  std::ostringstream shex;
87  shex << std::hex << fnum;
88  return shex.str();
89 }
90 
91 // decode the filenames placed in a location
93 {
95  std::vector< std::string > pos = utils::quoted_split(loc, ' ');
96  if(pos.empty())
97  return res;
98  std::vector< std::string >::const_iterator i = pos.begin(), end = pos.end();
99  while (true) {
100  res += get_filename(*(i++));
101  if(i == end) break;
102  res += ' ';
103  res += *(i++);
104  if(i == end) break;
105  res += ' ';
106  }
107  return res;
108 }
109 
110 
112  return value == v.value && arguments == v.arguments;
113 }
114 
116  if (location < v.location)
117  return true;
118  if (v.location < location)
119  return false;
120  return linenum < v.linenum;
121 }
122 
124 {
125 
126  const std::string key = "argument";
127 
128  writer.open_child(key);
129 
130  writer.write_key_val("name", arg);
131  writer.close_child(key);
132 }
133 
135 {
136 
137  const std::string key = "preproc_define";
138  writer.open_child(key);
139 
140  writer.write_key_val("name", name);
141  writer.write_key_val("value", value);
142  writer.write_key_val("textdomain", textdomain);
143  writer.write_key_val("linenum", std::to_string(linenum));
144  writer.write_key_val("location", get_location(location));
145 
146  for(const std::string &arg : arguments) {
147  write_argument(writer, arg);
148  }
149 
150  writer.close_child(key);
151 }
152 
154 {
155  arguments.push_back(cfg["name"]);
156 }
157 
158 void preproc_define::read(const config& cfg)
159 {
160  value = cfg["value"].str();
161  textdomain = cfg["textdomain"].str();
162  linenum = cfg["linenum"];
163  location = cfg["location"].str();
164 
165  for(const config &arg : cfg.child_range("argument")) {
166  read_argument(arg);
167  }
168 }
169 
170 preproc_map::value_type preproc_define::read_pair(const config &cfg)
171 {
172  preproc_define second;
173  second.read(cfg);
174  return preproc_map::value_type(cfg["name"], second);
175 }
176 
177 std::ostream& operator<<(std::ostream& stream, const preproc_define& def)
178 {
179  return stream << "value: " << def.value << " arguments: " << def.location;
180 }
181 
182 std::ostream& operator<<(std::ostream& stream, const preproc_map::value_type& def)
183 {
184  return stream << def.second;
185 }
186 
187 class preprocessor;
188 class preprocessor_file;
189 class preprocessor_data;
191 struct preprocessor_deleter;
192 
193 /**
194  * Base class for preprocessing an input.
195  */
197 {
202 protected:
205 public:
206  /**
207  * Preprocesses and sends some text to the #target_ buffer.
208  * @return false when the input has no data left.
209  */
211  virtual bool get_chunk() = 0;
212  ///retruns 1 if this parses a macro, -1 if this doesnt parse text (if this is no preprocessor_data). 0 otherwise (this parses a file).
213  virtual int is_macro() { return -1; }
214  virtual ~preprocessor();
215 };
216 
217 /**
218  * Target for sending preprocessed output.
219  * Objects of this class can be plugged into an STL stream.
220  */
221 class preprocessor_streambuf: public streambuf
222 {
223  std::string out_buffer_; /**< Buffer read by the STL stream. */
224  virtual int underflow();
225  std::stringstream buffer_; /**< Buffer filled by the #current_ preprocessor. */
226  preprocessor *current_; /**< Input preprocessor. */
231  int linenum_;
232  int depth_;
233  /**
234  * Set to true if one preprocessor for this target started to read a string.
235  * Deeper-nested preprocessors are then forbidden to.
236  */
237  bool quoted_;
238  friend class preprocessor;
239  friend class preprocessor_file;
240  friend class preprocessor_data;
241  friend struct preprocessor_deleter;
243 public:
246  void error(const std::string &, int);
247  void warning(const std::string &, int);
248 };
249 
250 // decode the filenames placed in a location
252 {
253  unsigned nested_level = 0;
254  preprocessor* pre = current_;
255  while(pre && pre->is_macro()) {
256  if(pre->is_macro() == 1) {
257  ++nested_level;
258  }
259  pre = pre->get_old_preprocessor();
260  }
262  std::vector< std::string > pos = utils::quoted_split(location_, ' ');
263  if(pos.size() <= 2*nested_level) {
264  return res;
265  }
266  else {
267  return get_filename(pos[2*nested_level]);
268  }
269 }
270 
272  streambuf(),
273  out_buffer_(""),
274  buffer_(),
275  current_(nullptr),
276  defines_(def),
277  default_defines_(),
278  textdomain_(PACKAGE),
279  location_(""),
280  linenum_(0),
281  depth_(0),
282  quoted_(false)
283 {
284 }
285 
287  streambuf(),
288  out_buffer_(""),
289  buffer_(),
290  current_(nullptr),
291  defines_(t.defines_),
292  default_defines_(),
293  textdomain_(PACKAGE),
294  location_(""),
295  linenum_(0),
296  depth_(t.depth_),
297  quoted_(t.quoted_)
298 {
299 }
300 
301 /**
302  * Called by an STL stream whenever it has reached the end of #out_buffer_.
303  * Fills #buffer_ by calling the #current_ preprocessor, then copies its
304  * content into #out_buffer_.
305  * @return the first character of #out_buffer_ if any, EOF otherwise.
306  */
308 {
309  unsigned sz = 0;
310  if (char *gp = gptr()) {
311  if (gp < egptr()) {
312  // Sanity check: the internal buffer has not been totally consumed,
313  // should we force the caller to use what remains first?
314  return *gp;
315  }
316  // The buffer has been completely read; fill it again.
317  // Keep part of the previous buffer, to ensure putback capabilities.
318  sz = out_buffer_.size();
319  buffer_.str(std::string());
320  if (sz > 3) {
321  buffer_ << out_buffer_.substr(sz - 3);
322  sz = 3;
323  }
324  else
325  {
326  buffer_ << out_buffer_;
327  }
328  } else {
329  // The internal get-data pointer is null
330  }
331  const int desired_fill_amount = 2000;
332  while (current_ && buffer_.rdbuf()->in_avail() < desired_fill_amount)
333  {
334  // Process files and data chunks until the desired buffer size is reached
335  if (!current_->get_chunk()) {
336  // Delete the current preprocessor item to restore its predecessor
337  delete current_;
338  }
339  }
340  // Update the internal state and data pointers
341  out_buffer_ = buffer_.str();
342  if(out_buffer_.empty()) {
343  return EOF;
344  }
345  char *begin = &*out_buffer_.begin();
346  unsigned bs = out_buffer_.size();
347  setg(begin, begin + sz, begin + bs);
348  if (sz >= bs)
349  return EOF;
350  return static_cast<unsigned char>(*(begin + sz));
351 }
352 
354 {
355  std::vector< std::string > pos = utils::quoted_split(lineno, ' ');
356  std::vector< std::string >::const_iterator i = pos.begin(), end = pos.end();
357  std::string included_from =
358  preprocessor_error_detail_prefix + "included from ";
360  while (i != end) {
361  std::string const &line = *(i++);
362  if (!res.empty())
363  res += included_from;
364  if (i != end)
365  res += get_filename(*(i++));
366  else
367  res += "<unknown>";
368  res += ':' + line;
369  }
370  if (res.empty()) res = "???";
371  return res;
372 }
373 
374 void preprocessor_streambuf::error(const std::string& error_type, int l)
375 {
376  std::string position, error;
377  std::ostringstream pos;
378  pos << l << ' ' << location_;
379  position = lineno_string(pos.str());
380  error = error_type + '\n';
381  error += "at " + position;
382  ERR_PREPROC << error << '\n';
383  throw preproc_config::error(error);
384 }
385 
386 void preprocessor_streambuf::warning(const std::string& warning_type, int l)
387 {
388  std::string position, warning;
389  std::ostringstream pos;
390  pos << l << ' ' << location_;
391  position = lineno_string(pos.str());
392  warning = warning_type + '\n';
393  warning += "at " + position;
394  WRN_PREPROC << warning << '\n';
395 }
396 
397 
398 /**
399  * Sets up a new preprocessor for stream buffer \a t.
400  * Saves the current preprocessing context of #target_. It will be
401  * automatically restored on destruction.
402  */
404  old_preprocessor_(t.current_),
405  old_textdomain_(t.textdomain_),
406  old_location_(t.location_),
407  old_linenum_(t.linenum_),
408  target_(t)
409 {
410  ++target_.depth_;
411  target_.current_ = this;
412 }
413 
414 /**
415  * Restores the old preprocessing context of #target_.
416  * Appends location and domain directives to the buffer, so that the parser
417  * notices these changes.
418  */
420 {
421  assert(target_.current_ == this);
422  if (!old_location_.empty()) {
423  target_.buffer_ << "\376line " << old_linenum_ << ' ' << old_location_ << '\n';
424  }
425  if (!old_textdomain_.empty() && target_.textdomain_ != old_textdomain_) {
426  target_.buffer_ << "\376textdomain " << old_textdomain_ << '\n';
427  }
432  --target_.depth_;
433 }
434 
435 /**
436  * Specialized preprocessor for handling a file or a set of files.
437  * A preprocessor_file object is created when a preprocessor encounters an
438  * inclusion directive that resolves to a file or directory, e.g. '{themes/}'.
439  */
441 {
442  std::vector< std::string > files_;
443  std::vector< std::string >::const_iterator pos_, end_;
444 public:
446  virtual bool get_chunk();
447 };
448 
449 /**
450  * Specialized preprocessor for handling any kind of input stream.
451  * This is the core of the preprocessor.
452  */
454 {
455  /** Description of a preprocessing chunk. */
456  struct token_desc
457  {
458  enum TOKEN_TYPE {
459  START, // Toplevel
460  PROCESS_IF, // Processing the "if" branch of a ifdef/ifndef (the "else" branch will be skipped)
461  PROCESS_ELSE, // Processing the "else" branch of a ifdef/ifndef
462  SKIP_IF, // Skipping the "if" branch of a ifdef/ifndef (the "else" branch, if any, will be processed)
463  SKIP_ELSE, // Skipping the "else" branch of a ifdef/ifndef
464  STRING, // Processing a string
465  VERBATIM, // Processing a verbatim string
466  MACRO_SPACE, // Processing between chunks of a macro call (skip spaces)
467  MACRO_CHUNK, // Processing inside a chunk of a macro call (stop on space or '(')
468  MACRO_PARENS // Processing a parenthesized macro argument
469  };
470  token_desc(TOKEN_TYPE type, const int stack_pos, const int linenum)
471  : type(type)
472  , stack_pos(stack_pos)
473  , linenum(linenum)
474  {
475  }
477  /** Starting position in #strings_ of the delayed text for this chunk. */
479  int linenum;
480  };
481 
482  /**
483  * Manages the lifetime of the @c std::istream pointer we own.
484  *
485  * Since @ref in_ uses the stream as well this object must be created
486  * before @ref in_ and destroyed after @ref in_ is destroyed.
487  */
489 
490  /** Input stream. */
492 
494  /** Buffer for delayed input processing. */
495  std::vector< std::string > strings_;
496  /** Mapping of macro arguments to their content. */
497  std::map<std::string, std::string> *local_defines_;
498  /** Stack of nested preprocessing chunks. */
499  std::vector< token_desc > tokens_;
500  /**
501  * Set to true whenever input tokens cannot be directly sent to the target
502  * buffer. For instance, this happens with macro arguments. In that case,
503  * the output is redirected toward #strings_ until it can be processed.
504  */
506  /**
507  * Non-zero when the preprocessor has to skip some input text.
508  * Increased whenever entering a conditional branch that is not useful,
509  * e.g. a ifdef that evaluates to false.
510  */
512  int linenum_;
513  ///true iff we are currently parsing a macros content, otherwise false.
515 
519  void skip_spaces();
520  void skip_eol();
522  void pop_token();
523  void put(char);
524  void put(std::string const & /*, int change_line
525  = 0 */);
526  void conditional_skip(bool skip);
527 public:
529  std::istream *,
530  std::string const &history,
531  std::string const &name, int line,
532  std::string const &dir, std::string const &domain,
533  std::map<std::string, std::string> *defines, bool is_define = false);
535  virtual bool get_chunk();
536  virtual int is_macro() { return is_define_ ? 1 : 0; }
541 };
542 
544 {
545  throw std::logic_error("don't compare tokens with characters");
546 }
547 bool operator==(char lhs, preprocessor_data::token_desc::TOKEN_TYPE rhs){ return rhs == lhs; }
548 bool operator!=(preprocessor_data::token_desc::TOKEN_TYPE rhs, char lhs){ return !(lhs == rhs); }
549 bool operator!=(char lhs, preprocessor_data::token_desc::TOKEN_TYPE rhs){ return rhs != lhs; }
550 
552  preprocessor(t),
553  files_(),
554  pos_(),
555  end_()
556 {
557  if (filesystem::is_directory(name)) {
558 
560 
561  for(std::string fname : files_) {
562  size_t cpos = fname.rfind(" ");
563  if (cpos != std::string::npos && cpos >= symbol_index) {
564  std::stringstream ss;
565  ss << "Found filename containing whitespace: '" << filesystem::base_name(fname) << "' in included directory '" << name << "'.\nThe included symbol probably looks similar to '"
566  << filesystem::directory_name(fname.substr(symbol_index)) << "'";
567  // TODO: find a real linenumber
568  target_.error(ss.str(), -1);
569  }
570  }
571  }
572  else {
573  std::istream * file_stream = filesystem::istream_file(name);
574  if (!file_stream->good()) {
575  ERR_PREPROC << "Could not open file " << name << std::endl;
576  delete file_stream;
577  }
578  else
579  new preprocessor_data(t, file_stream, "", filesystem::get_short_wml_path(name),
580  1, filesystem::directory_name(name), t.textdomain_, nullptr);
581  }
582  pos_ = files_.begin();
583  end_ = files_.end();
584 }
585 
586 /**
587  * preprocessor_file::get_chunk()
588  *
589  * Inserts and processes the next file in the list of included files.
590  * @return false if there is no next file.
591  */
593 {
594  while (pos_ != end_) {
595  const std::string &name = *(pos_++);
596  unsigned sz = name.size();
597  // Use reverse iterator to optimize testing
598  if (sz < 5 || !std::equal(name.rbegin(), name.rbegin() + 4, "gfc."))
599  continue;
600  new preprocessor_file(target_, name);
601  return true;
602  }
603  return false;
604 }
605 
607  std::istream *i, std::string const &history, std::string const &name, int linenum,
608  std::string const &directory, std::string const &domain,
609  std::map<std::string, std::string> *defines, bool is_define) :
610  preprocessor(t),
611  in_scope_(i),
612  in_(*i),
613  directory_(directory),
614  strings_(),
615  local_defines_(defines),
616  tokens_(),
617  slowpath_(0),
618  skipping_(0),
619  linenum_(linenum),
620  is_define_(is_define)
621 {
622  std::ostringstream s;
623 
624  s << history;
625  if (!name.empty()) {
626  if (!history.empty())
627  s << ' ';
628 
629  s << get_file_code(name);
630  }
631 
632  if (!t.location_.empty())
633  s << ' ' << t.linenum_ << ' ' << t.location_;
634  t.location_ = s.str();
635  t.linenum_ = linenum;
636 
637  t.buffer_ << "\376line " << linenum << ' ' << t.location_ << '\n';
638  if (t.textdomain_ != domain) {
639  t.buffer_ << "\376textdomain " << domain << '\n';
640  t.textdomain_ = domain;
641  }
642 
644 }
645 
647 {
648  delete local_defines_;
649 }
650 
652 {
653  token_desc token(t, strings_.size(), linenum_);
654  tokens_.push_back(token);
655  if (t == token_desc::MACRO_SPACE) {
656  // Macro expansions do not have any associated storage at start.
657  return;
658  } else if (t == token_desc::STRING || t == token_desc::VERBATIM) {
659  /* Quoted strings are always inlined in the parent token. So
660  they need neither storage nor metadata, unless the parent
661  token is a macro expansion. */
662  token_desc::TOKEN_TYPE &outer_type = tokens_[tokens_.size() - 2].type;
663  if (outer_type != token_desc::MACRO_SPACE)
664  return;
665  outer_type = token_desc::MACRO_CHUNK;
666  tokens_.back().stack_pos = strings_.size() + 1;
667  }
668  std::ostringstream s;
669  if (!skipping_ && slowpath_) {
670  s << "\376line " << linenum_ << ' ' << target_.location_
671  << "\n\376textdomain " << target_.textdomain_ << '\n';
672  }
673  strings_.push_back(s.str());
674 }
675 
677 {
678  token_desc::TOKEN_TYPE inner_type = tokens_.back().type;
679  unsigned stack_pos = tokens_.back().stack_pos;
680  tokens_.pop_back();
681  token_desc::TOKEN_TYPE &outer_type = tokens_.back().type;
682  if (inner_type == token_desc::MACRO_PARENS) {
683  // Parenthesized macro arguments are left on the stack.
684  assert(outer_type == token_desc::MACRO_SPACE);
685  return;
686  }
687  if (inner_type == token_desc::STRING || inner_type == token_desc::VERBATIM) {
688  // Quoted strings are always inlined.
689  assert(stack_pos == strings_.size());
690  return;
691  }
692  if (outer_type == token_desc::MACRO_SPACE) {
693  /* A macro expansion does not have any associated storage.
694  Instead, storage of the inner token is not discarded
695  but kept as a new macro argument. But if the inner token
696  was a macro expansion, it is about to be appended, so
697  prepare for it. */
698  if (inner_type == token_desc::MACRO_SPACE || inner_type == token_desc::MACRO_CHUNK) {
699  strings_.erase(strings_.begin() + stack_pos, strings_.end());
700  strings_.push_back(std::string());
701  }
702  assert(stack_pos + 1 == strings_.size());
703  outer_type = token_desc::MACRO_CHUNK;
704  return;
705  }
706  strings_.erase(strings_.begin() + stack_pos, strings_.end());
707 }
708 
710 {
711  for(;;) {
712  int c = in_.peek();
713  if (in_.eof() || (c != ' ' && c != '\t'))
714  return;
715  in_.get();
716  }
717 }
718 
720 {
721  for(;;) {
722  int c = in_.get();
723  if (c == '\n') {
724  ++linenum_;
725  return;
726  }
727  if (in_.eof())
728  return;
729  }
730 }
731 
733 {
735  for(;;) {
736  int c = in_.peek();
737  if (c == preprocessor_streambuf::traits_type::eof() || utils::portable_isspace(c)) {
738  // DBG_PREPROC << "(" << res << ")\n";
739  return res;
740  }
741  in_.get();
742  res += static_cast<char>(c);
743  }
744 }
745 
747 {
749  for(;;) {
750  int c = in_.get();
751  if (c == '\n') {
752  ++linenum_;
753  return res;
754  }
755  if (in_.eof())
756  return res;
757  if (c != '\r')
758  res += static_cast<char>(c);
759  }
760 }
761 
763 {
765  while(in_.peek() != '\n' && !in_.eof()) {
766  int c = in_.get();
767  if (c != '\r')
768  res += static_cast<char>(c);
769  }
770  return res;
771 }
772 
774 {
775  if (skipping_)
776  return;
777  if (slowpath_) {
778  strings_.back() += c;
779  return;
780  }
781 
782  int cond_linenum = c == '\n' ? linenum_ - 1 : linenum_;
783  if (unsigned diff = cond_linenum - target_.linenum_)
784  {
785  target_.linenum_ = cond_linenum;
786  if (diff <= target_.location_.size() + 11) {
787  target_.buffer_ << std::string(diff, '\n');
788  } else {
789  target_.buffer_ << "\376line " << target_.linenum_
790  << ' ' << target_.location_ << '\n';
791  }
792  }
793 
794  if (c == '\n')
795  ++target_.linenum_;
796  target_.buffer_ << c;
797 }
798 
799 void preprocessor_data::put(std::string const &s /*, int line_change*/)
800 {
801  if (skipping_)
802  return;
803  if (slowpath_) {
804  strings_.back() += s;
805  return;
806  }
807  target_.buffer_ << s;
808 // target_.linenum_ += line_change;
809 }
810 
812 {
813  if (skip) ++skipping_;
815 }
816 
818 {
819  char c = in_.get();
820  token_desc &token = tokens_.back();
821  if (in_.eof()) {
822  // The end of file was reached.
823  // Make sure we don't have any incomplete tokens.
824  char const *s;
825  switch (token.type) {
826  case token_desc::START: return false; // everything is fine
828  case token_desc::SKIP_IF:
830  case token_desc::SKIP_ELSE: s = "#ifdef or #ifndef"; break;
831  case token_desc::STRING: s = "Quoted string"; break;
832  case token_desc::VERBATIM: s = "Verbatim string"; break;
834  case token_desc::MACRO_SPACE: s = "Macro substitution"; break;
835  case token_desc::MACRO_PARENS: s = "Macro argument"; break;
836  default: s = "???";
837  }
838  target_.error(std::string(s) + " not terminated", token.linenum);
839  }
840  if (c == '\n')
841  ++linenum_;
842  if (c == '\376') {
843  std::string buffer(1, c);
844  for(;;) {
845  char d = in_.get();
846  if (in_.eof() || d == '\n')
847  break;
848  buffer += d;
849  }
850  buffer += '\n';
851  // line_change = 1-1 = 0
852  put(buffer);
853  } else if (token.type == token_desc::VERBATIM) {
854  put(c);
855  if (c == '>' && in_.peek() == '>') {
856  put(in_.get());
857  pop_token();
858  }
859  } else if (c == '<' && in_.peek() == '<') {
860  in_.get();
862  put('<');
863  put('<');
864  } else if (c == '"') {
865  if (token.type == token_desc::STRING) {
866  target_.quoted_ = false;
867  put(c);
868  pop_token();
869  } else if (!target_.quoted_) {
870  target_.quoted_ = true;
872  put(c);
873  } else {
874  target_.error("Nested quoted string" , linenum_);
875  }
876  } else if (c == '{') {
878  ++slowpath_;
879  } else if (c == ')' && token.type == token_desc::MACRO_PARENS) {
880  pop_token();
881  } else if (c == '#' && !target_.quoted_) {
882  std::string command = read_word();
883  bool comment = false;
884  if (command == "define") {
885  skip_spaces();
886  int linenum = linenum_;
887  std::vector< std::string > items = utils::split(read_line(), ' ');
888  if (items.empty()) {
889  target_.error("No macro name found after #define directive", linenum);
890  }
891  std::string symbol = items.front();
892  items.erase(items.begin());
893  int found_enddef = 0;
895  for(;;) {
896  if (in_.eof())
897  break;
898  char d = in_.get();
899  if (d == '\n')
900  ++linenum_;
901  buffer += d;
902  if (d == '#')
903  found_enddef = 1;
904  else if (found_enddef > 0)
905  if (++found_enddef == 7) {
906  if (std::equal(buffer.end() - 6, buffer.end(), "enddef"))
907  break;
908  else {
909  found_enddef = 0;
910  if (std::equal(buffer.end()-6, buffer.end(), "define")) { //TODO: Maybe add support for this? This would fill feature request #21343
911  target_.error("Preprocessor error: #define is not allowed inside a #define/#enddef pair", linenum);
912  }
913  }
914  }
915  }
916  if (found_enddef != 7) {
917  target_.error("Unterminated preprocessor definition", linenum_);
918  }
919  if (!skipping_) {
920  preproc_map::const_iterator old_i = target_.defines_->find(symbol);
921  if (old_i != target_.defines_->end()) {
922  std::ostringstream new_pos, old_pos;
923  const preproc_define& old_d = old_i->second;
924 
925  new_pos << linenum << ' ' << target_.location_;
926  old_pos << old_d.linenum << ' ' << old_d.location;
927 
928  WRN_PREPROC << "Redefining macro " << symbol
929  << " without explicit #undef at "
930  << lineno_string(new_pos.str()) << '\n'
931  << "previously defined at "
932  << lineno_string(old_pos.str()) << '\n';
933  }
934 
935  buffer.erase(buffer.end() - 7, buffer.end());
936  (*target_.defines_)[symbol] = preproc_define(buffer, items, target_.textdomain_,
937  linenum, target_.location_);
938  LOG_PREPROC << "defining macro " << symbol << " (location " << get_location(target_.location_) << ")\n";
939  }
940  } else if (command == "ifdef") {
941  skip_spaces();
942  std::string const &symbol = read_word();
943  bool found = target_.defines_->count(symbol) != 0;
944  DBG_PREPROC << "testing for macro " << symbol << ": "
945  << (found ? "defined" : "not defined") << '\n';
946  conditional_skip(!found);
947  } else if (command == "ifndef") {
948  skip_spaces();
949  std::string const &symbol = read_word();
950  bool found = target_.defines_->count(symbol) != 0;
951  DBG_PREPROC << "testing for macro " << symbol << ": "
952  << (found ? "defined" : "not defined") << '\n';
953  conditional_skip(found);
954  } else if (command == "ifhave") {
955  skip_spaces();
956  std::string const &symbol = read_word();
957  bool found = !filesystem::get_wml_location(symbol, directory_).empty();
958  DBG_PREPROC << "testing for file or directory " << symbol << ": "
959  << (found ? "found" : "not found") << '\n';
960  conditional_skip(!found);
961  } else if (command == "ifnhave") {
962  skip_spaces();
963  std::string const &symbol = read_word();
964  bool found = !filesystem::get_wml_location(symbol, directory_).empty();
965  DBG_PREPROC << "testing for file or directory " << symbol << ": "
966  << (found ? "found" : "not found") << '\n';
967  conditional_skip(found);
968  } else if (command == "ifver" || command == "ifnver") {
969  skip_spaces();
970  std::string const& vsymstr = read_word();
971  skip_spaces();
972  std::string const& vopstr = read_word();
973  skip_spaces();
974  std::string const& vverstr = read_word();
975 
976  const VERSION_COMP_OP vop = parse_version_op(vopstr);
977 
978  if(vop == OP_INVALID) {
979  target_.error("Invalid #ifver/#ifnver operator", linenum_);
980  } else if(target_.defines_->count(vsymstr) != 0) {
981  preproc_define const& sym = (*target_.defines_)[vsymstr];
982 
983  if(!sym.arguments.empty()) {
984  target_.error("First argument macro in #ifver/#ifnver should not require arguments", linenum_);
985  }
986 
987  version_info const version1(sym.value);
988  version_info const version2(vverstr);
989 
990  const bool found = do_version_check(version1, vop, version2);
991  DBG_PREPROC << "testing version '" << version1.str() << "' against '" << version2.str() << "' (" << vopstr << "): "
992  << (found ? "match" : "no match") << '\n';
993 
994  conditional_skip(command == "ifver" ? !found : found);
995  } else {
996  std::string err = "Undefined macro in #ifver/#ifnver first argument: '";
997  err += vsymstr;
998  err += "'";
999  target_.error(err, linenum_);
1000  }
1001  } else if (command == "else") {
1002  if (token.type == token_desc::SKIP_ELSE) {
1003  pop_token();
1004  --skipping_;
1006  } else if (token.type == token_desc::PROCESS_IF) {
1007  pop_token();
1008  ++skipping_;
1010  } else {
1011  target_.error("Unexpected #else", linenum_);
1012  }
1013  } else if (command == "endif") {
1014  switch (token.type) {
1015  case token_desc::SKIP_IF:
1018  case token_desc::PROCESS_ELSE: break;
1019  default:
1020  target_.error("Unexpected #endif", linenum_);
1021  }
1022  pop_token();
1023  } else if (command == "textdomain") {
1024  skip_spaces();
1025  std::string const &s = read_word();
1026  if (s != target_.textdomain_) {
1027  put("#textdomain ");
1028  put(s);
1029  target_.textdomain_ = s;
1030  }
1031  comment = true;
1032  } else if (command == "enddef") {
1033  target_.error("Unexpected #enddef", linenum_);
1034  } else if (command == "undef") {
1035  skip_spaces();
1036  std::string const &symbol = read_word();
1037  if (!skipping_) {
1038  target_.defines_->erase(symbol);
1039  LOG_PREPROC << "undefine macro " << symbol << " (location " << get_location(target_.location_) << ")\n";
1040  }
1041  } else if (command == "error") {
1042  if (!skipping_) {
1043  skip_spaces();
1044  std::ostringstream error;
1045  error << "#error: \"" << read_rest_of_line() << '"';
1046  target_.error(error.str(), linenum_);
1047  } else
1048  DBG_PREPROC << "Skipped an error\n";
1049  } else if (command == "warning") {
1050  if (!skipping_) {
1051  skip_spaces();
1052  std::ostringstream warning;
1053  warning << "#warning: \"" << read_rest_of_line() << '"';
1054  target_.warning(warning.str(), linenum_);
1055  } else
1056  DBG_PREPROC << "Skipped a warning\n";
1057  } else
1058  comment = token.type != token_desc::MACRO_SPACE;
1059  skip_eol();
1060  if (comment)
1061  put('\n');
1062  } else if (token.type == token_desc::MACRO_SPACE || token.type == token_desc::MACRO_CHUNK) {
1063  if (c == '(') {
1064  // If a macro argument was started, it is implicitly ended.
1065  token.type = token_desc::MACRO_SPACE;
1067  } else if (utils::portable_isspace(c)) {
1068  // If a macro argument was started, it is implicitly ended.
1069  token.type = token_desc::MACRO_SPACE;
1070  } else if (c == '}') {
1071  --slowpath_;
1072  if (skipping_) {
1073  pop_token();
1074  return true;
1075  }
1076  // FIXME: is this obsolete?
1077  //if (token.type == token_desc::MACRO_SPACE) {
1078  // if (!strings_.back().empty()) {
1079  // std::ostringstream error;
1080  // std::ostringstream location;
1081  // error << "Can't parse new macro parameter with a macro call scope open";
1082  // location<<linenum_<<' '<<target_.location_;
1083  // target_.error(error.str(), location.str());
1084  // }
1085  // strings_.pop_back();
1086  //}
1087 
1088  if(strings_.size() <= static_cast<size_t>(token.stack_pos)) {
1089  target_.error("No macro or file substitution target specified", linenum_);
1090  }
1091 
1092  std::string symbol = strings_[token.stack_pos];
1093  std::string::size_type pos;
1094  while ((pos = symbol.find('\376')) != std::string::npos) {
1095  std::string::iterator b = symbol.begin(); // invalidated at each iteration
1096  symbol.erase(b + pos, b + symbol.find('\n', pos + 1) + 1);
1097  }
1098  std::map<std::string, std::string>::const_iterator arg;
1099  preproc_map::const_iterator macro;
1100  // If this is a known pre-processing symbol, then we insert it,
1101  // otherwise we assume it's a file name to load.
1102  if(symbol == current_file_str && strings_.size() - token.stack_pos == 1) {
1103  pop_token();
1105  }
1106  else if(symbol == current_dir_str && strings_.size() - token.stack_pos == 1) {
1107  pop_token();
1109  }
1110  else if (local_defines_ &&
1111  (arg = local_defines_->find(symbol)) != local_defines_->end())
1112  {
1113  if (strings_.size() - token.stack_pos != 1)
1114  {
1115  std::ostringstream error;
1116  error << "Macro argument '" << symbol
1117  << "' does not expect any arguments";
1118  target_.error(error.str(), linenum_);
1119  }
1120  std::ostringstream v;
1121  v << arg->second << "\376line " << linenum_ << ' ' << target_.location_
1122  << "\n\376textdomain " << target_.textdomain_ << '\n';
1123  pop_token();
1124  put(v.str());
1125  }
1126  else if (target_.depth_ < 100 && (macro = target_.defines_->find(symbol)) != target_.defines_->end())
1127  {
1128  preproc_define const &val = macro->second;
1129  size_t nb_arg = strings_.size() - token.stack_pos - 1;
1130  if (nb_arg != val.arguments.size())
1131  {
1132  const std::vector<std::string>& locations = utils::quoted_split(val.location, ' ');
1133  std::ostringstream error;
1134  error << "Preprocessor symbol '" << symbol << "' defined at "
1135  << get_filename(locations[0]) << ":" << val.linenum << " expects "
1136  << val.arguments.size() << " arguments, but has "
1137  << nb_arg << " arguments";
1138  target_.error(error.str(), linenum_);
1139  }
1140  std::istringstream *buffer = new std::istringstream(val.value);
1141  std::map<std::string, std::string> *defines =
1142  new std::map<std::string, std::string>;
1143  for (size_t i = 0; i < nb_arg; ++i) {
1144  (*defines)[val.arguments[i]] = strings_[token.stack_pos + i + 1];
1145  }
1146  pop_token();
1147  std::string const &dir = filesystem::directory_name(val.location.substr(0, val.location.find(' ')));
1148  if (!slowpath_) {
1149  DBG_PREPROC << "substituting macro " << symbol << '\n';
1150  new preprocessor_data(target_, buffer, val.location, "",
1151  val.linenum, dir, val.textdomain, defines, true);
1152  } else {
1153  DBG_PREPROC << "substituting (slow) macro " << symbol << '\n';
1154  std::ostringstream res;
1157  // Make the nested preprocessor_data responsible for
1158  // restoring our current textdomain if needed.
1160  { std::istream in(buf);
1161  new preprocessor_data(*buf, buffer, val.location, "",
1162  val.linenum, dir, val.textdomain, defines, true);
1163  res << in.rdbuf(); }
1164  delete buf;
1165  put(res.str());
1166  }
1167  } else if (target_.depth_ < 40) {
1168  LOG_PREPROC << "Macro definition not found for " << symbol << " , attempting to open as file.\n";
1169  pop_token();
1171  if (!nfname.empty())
1172  {
1173  if (!slowpath_)
1174  // nfname.size() - symbol.size() gives you an index into nfname
1175  // This does not necessarily match the symbol though, as it can start with ~ or ./
1176  new preprocessor_file(target_, nfname, nfname.size() - symbol.size());
1177  else {
1178  std::ostringstream res;
1181  { std::istream in(buf);
1182  new preprocessor_file(*buf, nfname, nfname.size() - symbol.size());
1183  res << in.rdbuf(); }
1184  delete buf;
1185  put(res.str());
1186  }
1187  }
1188  else
1189  {
1190  std::ostringstream error;
1191  error << "Macro/file '" << symbol << "' is missing";
1192  target_.error(error.str(), linenum_);
1193  }
1194  } else {
1195  target_.error("Too many nested preprocessing inclusions", linenum_);
1196  }
1197  }
1198  else if (!skipping_)
1199  {
1200  if (token.type == token_desc::MACRO_SPACE)
1201  {
1202  std::ostringstream s;
1203  s << "\376line " << linenum_ << ' ' << target_.location_
1204  << "\n\376textdomain " << target_.textdomain_ << '\n';
1205  strings_.push_back(s.str());
1206  token.type = token_desc::MACRO_CHUNK;
1207  }
1208  put(c);
1209  }
1210  } else
1211  put(c);
1212  return true;
1213 }
1214 
1215 struct preprocessor_deleter: std::basic_istream<char>
1216 {
1221 };
1222 
1224  preproc_map *defines)
1225  : std::basic_istream<char>(buf), buf_(buf), defines_(defines)
1226 {
1227 }
1228 
1230 {
1231  clear(std::ios_base::goodbit);
1232  exceptions(std::ios_base::goodbit);
1233  rdbuf(nullptr);
1234  delete buf_;
1235  delete defines_;
1236 }
1237 
1238 std::istream *preprocess_file(std::string const &fname, preproc_map *defines)
1239 {
1240  log_scope("preprocessing file " + fname + " ...");
1241  preproc_map *owned_defines = nullptr;
1242  if (!defines) {
1243  // If no preproc_map has been given, create a new one,
1244  // and ensure it is destroyed when the stream is
1245 // ??
1246  // by giving it to the deleter.
1247  owned_defines = new preproc_map;
1248  defines = owned_defines;
1249  }
1251 
1252  new preprocessor_file(*buf, fname);
1253  return new preprocessor_deleter(buf, owned_defines);
1254 }
1255 
1256 void preprocess_resource(const std::string& res_name, preproc_map *defines_map,
1257  bool write_cfg, bool write_plain_cfg,std::string target_directory)
1258 {
1259  if (filesystem::is_directory(res_name))
1260  {
1261  std::vector<std::string> dirs,files;
1262 
1264 
1265  // subdirectories
1266  for(const std::string& dir : dirs)
1267  {
1268  LOG_PREPROC << "processing sub-dir: " << dir << '\n';
1269  preprocess_resource(dir, defines_map, write_cfg, write_plain_cfg, target_directory);
1270  }
1271 
1272  // files in current directory
1273  for(const std::string& file : files)
1274  {
1275  preprocess_resource(file, defines_map, write_cfg, write_plain_cfg, target_directory);
1276  }
1277  return;
1278  }
1279 
1280  // process only config files.
1281  if (filesystem::ends_with(res_name, ".cfg") == false)
1282  return;
1283 
1284  LOG_PREPROC << "processing resource: " << res_name << '\n';
1285 
1286  //disable filename encoding to get clear #line in cfg.plain
1287  encode_filename = false;
1288 
1289  filesystem::scoped_istream stream = preprocess_file(res_name, defines_map);
1290  std::stringstream ss;
1291  // Set the failbit so if we get any preprocessor exceptions (e.g.:preproc_config::error)
1292  // they will be propagated in the main program, instead of just setting the
1293  // failbit on the stream. This was necessary in order for the MSVC and GCC
1294  // binaries to behave the same way.
1295  ss.exceptions(std::ios_base::failbit);
1296 
1297  ss << (*stream).rdbuf();
1298 
1299  LOG_PREPROC << "processing finished\n";
1300 
1301  if (write_cfg == true || write_plain_cfg == true)
1302  {
1303  config cfg;
1304  std::string streamContent = ss.str();
1305 
1306  read(cfg, streamContent);
1307  const std::string preproc_res_name = target_directory + "/" + filesystem::base_name(res_name);
1308 
1309  // write the processed cfg file
1310  if (write_cfg == true)
1311  {
1312  LOG_PREPROC << "writing cfg file: " << preproc_res_name << '\n';
1314  filesystem::scoped_ostream outStream(filesystem::ostream_file(preproc_res_name));
1315  write(*outStream, cfg);
1316  }
1317 
1318  // write the plain cfg file
1319  if (write_plain_cfg == true)
1320  {
1321  LOG_PREPROC << "writing plain cfg file: " << (preproc_res_name + ".plain") << '\n';
1323  filesystem::write_file(preproc_res_name + ".plain", streamContent);
1324  }
1325  }
1326 }
static std::string current_file_str
std::string lineno_string(const std::string &lineno)
child_itors child_range(const std::string &key)
Definition: config.cpp:613
std::vector< std::string >::const_iterator end_
std::map< std::string, std::string > * local_defines_
Mapping of macro arguments to their content.
bool operator==(preproc_define const &) const
preprocessor_data(preprocessor_streambuf &, std::istream *, std::string const &history, std::string const &name, int line, std::string const &dir, std::string const &domain, std::map< std::string, std::string > *defines, bool is_define=false)
preprocessor_streambuf & target_
virtual ~preprocessor()
Restores the old preprocessing context of target_.
VERSION_COMP_OP parse_version_op(const std::string &op_str)
Definition: version.cpp:276
Specialized preprocessor for handling a file or a set of files.
void read(const config &)
const GLfloat * c
Definition: glew.h:12741
int pos
Definition: formula.cpp:800
std::string read_word()
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
preproc_map default_defines_
virtual int is_macro()
retruns 1 if this parses a macro, -1 if this doesnt parse text (if this is no preprocessor_data). 0 otherwise (this parses a file).
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
buffered_istream in_
Input stream.
bool ends_with(const std::string &str, const std::string &suffix)
#define ERR_PREPROC
preprocessor_streambuf * buf_
int peek()
Gets a character from the buffer.
Specialized preprocessor for handling any kind of input stream.
std::string out_buffer_
Buffer read by the STL stream.
void clear(const std::string &key)
Helper class for buffering a std::istream.
void write_argument(config_writer &, const std::string &) const
GLuint const GLfloat * val
Definition: glew.h:2614
bool operator<(preproc_define const &) const
bool eof() const
Is the end of input reached?
STL namespace.
void read_argument(const config &)
const std::vector< std::string > items
#define d
GLdouble GLdouble t
Definition: glew.h:1366
Definitions for the interface to Wesnoth Markup Language (WML).
std::string location
Description of a preprocessing chunk.
std::map< std::string, preproc_define > preproc_map
GLuint in
Definition: glew.h:9261
GLdouble l
Definition: glew.h:6966
std::vector< std::string >::const_iterator pos_
#define WRN_PREPROC
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
GLuint GLuint stream
Definition: glew.h:5239
int slowpath_
Set to true whenever input tokens cannot be directly sent to the target buffer.
bool is_define_
true iff we are currently parsing a macros content, otherwise false.
void write_key_val(const std::string &key, const T &value)
This template function will work with any type that can be assigned to an attribute_value.
GLuint GLuint end
Definition: glew.h:1221
int get()
Gets and consumes a character from the buffer.
void conditional_skip(bool skip)
bool quoted_
Set to true if one preprocessor for this target started to read a string.
void close_child(const std::string &key)
bool create_directory_if_missing_recursive(const std::string &dirname)
Creates a recursive directory tree if it does not exist already.
static lg::log_domain log_preprocessor("preprocessor")
preprocessor_file(preprocessor_streambuf &, std::string const &, size_t)
Class for writing a config out to a file in pieces.
std::vector< token_desc > tokens_
Stack of nested preprocessing chunks.
std::string read_line()
void write_file(const std::string &fname, const std::string &data)
Throws io_exception if an error occurs.
const GLdouble * v
Definition: glew.h:1359
GLsizei const GLfloat * value
Definition: glew.h:1817
int skipping_
Non-zero when the preprocessor has to skip some input text.
void push_token(token_desc::TOKEN_TYPE)
std::string base_name(const std::string &file)
Returns the base filename of a file, with directory name stripped.
std::istream * istream_file(const std::string &fname, bool treat_failure_as_error=true)
void open_child(const std::string &key)
std::ostream * ostream_file(std::string const &fname, bool create_directory=true)
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
GLenum GLuint GLsizei const char * buf
Definition: glew.h:2498
std::string get_short_wml_path(const std::string &filename)
Returns a short path to filename, skipping the (user) data directory.
#define DBG_PREPROC
std::vector< std::string > strings_
Buffer for delayed input processing.
std::map< std::string, int > t_file_number_map
std::string value
preprocessor * current_
Input preprocessor.
#define LOG_PREPROC
GLfloat GLfloat p
Definition: glew.h:12766
Templates and utility-routines for strings and numbers.
GLsizei const GLint * locations
Definition: glew.h:11075
std::string str() const
Serializes the version number into string form.
Definition: version.cpp:90
GLuint buffer
Definition: glew.h:1648
Encapsulates the map of the game.
Definition: location.hpp:38
std::string escape(const std::string &str, const char *special_chars)
Prepends a configurable set of characters with a backslash.
#define PACKAGE
Definition: wesconfig.h:23
std::string directory_
virtual int is_macro()
retruns 1 if this parses a macro, -1 if this doesnt parse text (if this is no preprocessor_data). 0 otherwise (this parses a file).
static std::string get_file_code(const std::string &filename)
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.
std::vector< std::string > quoted_split(std::string const &val, char c, int flags, char quote)
This function is identical to split(), except it does not split when it otherwise would if the previo...
GLuint res
Definition: glew.h:9258
std::vector< std::string > arguments
preprocessor_deleter(preprocessor_streambuf *buf, preproc_map *defines)
token_desc(TOKEN_TYPE type, const int stack_pos, const int linenum)
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, file_name_option mode=FILE_NAME_ONLY, file_filter_option filter=NO_FILTER, file_reorder_option reorder=DONT_REORDER, file_tree_checksum *checksum=nullptr)
Populates 'files' with all the files and 'dirs' with all the directories in dir.
VERSION_COMP_OP
Definition: version.hpp:195
logger & err()
Definition: log.cpp:79
std::string get_wml_location(const std::string &filename, const std::string &current_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
std::string read_rest_of_line()
bool operator!=(preprocessor_data::token_desc::TOKEN_TYPE rhs, char lhs)
static std::string get_location(const std::string &loc)
filesystem::scoped_istream in_scope_
Manages the lifetime of the std::istream pointer we own.
virtual int underflow()
Called by an STL stream whenever it has reached the end of out_buffer_.
#define log_scope(description)
Definition: log.hpp:185
size_t i
Definition: function.cpp:1057
void write(config_writer &, const std::string &) const
Helper class for buffering a std::istream.
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:400
static int writer(lua_State *L, const void *b, size_t size, void *B)
Definition: lstrlib.cpp:166
void warning(const std::string &, int)
Base class for preprocessing an input.
preprocessor_streambuf(preprocessor_streambuf const &)
Represents version numbers.
Definition: version.hpp:44
std::ostream & operator<<(std::ostream &stream, const preproc_define &def)
static std::string current_dir_str
virtual bool get_chunk()
preprocessor_file::get_chunk()
GLuint const GLchar * name
Definition: glew.h:1782
static std::string get_filename(const std::string &file_code)
std::vector< std::string > files_
std::istream * preprocess_file(std::string const &fname, preproc_map *defines)
GLclampd n
Definition: glew.h:5903
Standard logging facilities (interface).
std::string textdomain
virtual bool get_chunk()=0
preprocessor(preprocessor_streambuf &)
Sets up a new preprocessor for stream buffer t.
int stack_pos
Starting position in strings_ of the delayed text for this chunk.
#define c
Definition: glew.h:12743
friend bool operator==(preprocessor_data::token_desc::TOKEN_TYPE, char)
void error(const std::string &, int)
Target for sending preprocessed output.
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
bool do_version_check(const version_info &a, VERSION_COMP_OP op, const version_info &b)
Definition: version.cpp:295
bool portable_isspace(const char c)
bool operator==(preprocessor_data::token_desc::TOKEN_TYPE, char)
std::string get_current_file()
preprocessor * get_old_preprocessor()
Preprocesses and sends some text to the target_ buffer.
preproc_map * defines_
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
std::string old_location_
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
static preproc_map::value_type read_pair(const config &)
preprocessor *const old_preprocessor_
static t_file_number_map file_number_map
GLdouble s
Definition: glew.h:1358
std::string old_textdomain_
Interfaces for manipulating version numbers of engine, add-ons, etc.
void write(std::ostream &out, configr_of const &cfg, unsigned int level)
Definition: parser.cpp:621
static bool encode_filename
GLsizei const GLcharARB ** string
Definition: glew.h:4503
void preprocess_resource(const std::string &res_name, preproc_map *defines_map, bool write_cfg, bool write_plain_cfg, std::string target_directory)
std::stringstream buffer_
Buffer filled by the current_ preprocessor.
virtual bool get_chunk()
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
friend bool operator!=(preprocessor_data::token_desc::TOKEN_TYPE, char)
static std::string preprocessor_error_detail_prefix