38 #define ERR_PREPROC LOG_STREAM(err, log_preprocessor) 
   39 #define WRN_PREPROC LOG_STREAM(warn, log_preprocessor) 
   40 #define LOG_PREPROC LOG_STREAM(info, log_preprocessor) 
   41 #define DBG_PREPROC LOG_STREAM(debug, log_preprocessor) 
   84     static int current_file_number = 0;
 
   88         fnum = ++current_file_number;
 
   91     std::ostringstream shex;
 
   92     shex << std::hex << fnum;
 
  107     std::vector<std::string>::const_iterator 
i = pos.begin(), end = pos.end();
 
  153     const std::string key = 
"argument";
 
  163     const std::string key = 
"argument";
 
  174     const std::string key = 
"preproc_define";
 
  191     for(
const std::string& arg : 
arguments) {
 
  231     map.try_emplace(
cfg[
"name"], 
cfg);
 
  236     return stream << 
"value: " << def.
value << 
" arguments: " << def.
location;
 
  239 std::ostream& 
operator<<(std::ostream& stream, 
const preproc_map::value_type& def)
 
  241     return stream << def.second;
 
  327     void error(
const std::string&, 
int);
 
  328     void warning(
const std::string&, 
int);
 
  330     template<
typename T, 
typename... A>
 
  402     , old_textdomain_(
t.textdomain_)
 
  403     , old_location_(
t.location_)
 
  404     , old_linenum_(
t.linenum_)
 
  417     if(
char* gp = gptr()) {
 
  439     const int desired_fill_amount = 2000;
 
  441     while(
current() && 
buffer_.rdbuf()->in_avail() < desired_fill_amount) {
 
  458     setg(begin, begin + sz, begin + bs);
 
  464     return static_cast<unsigned char>(*(begin + sz));
 
  494     unsigned nested_level = 0;
 
  514     if(pos.size() <= 2 * nested_level) {
 
  524     std::vector<std::string>::const_iterator 
i = pos.begin(), end = pos.end();
 
  529         const std::string& 
line = *(
i++);
 
  532             res += included_from;
 
  553     std::string position, 
error;
 
  554     std::ostringstream pos;
 
  559     error = error_type + 
'\n';
 
  560     error += 
"at " + position;
 
  570     std::ostringstream pos;
 
  597     virtual void init() 
override;
 
  606             const std::string& name = *(
pos_++);
 
  607             unsigned sz = name.size();
 
  610             if(sz < 5 || !std::equal(name.rbegin(), name.rbegin() + 4, 
"gfc.")) {
 
  623     std::vector<std::string>::const_iterator 
pos_, 
end_;
 
  720     void put(
const std::string& );
 
  726             const std::string& history,
 
  727             const std::string& name,
 
  729             const std::string& dir,
 
  730             const std::string& domain,
 
  731             std::unique_ptr<std::map<std::string, std::string>> defines,
 
  732             bool is_define = 
false);
 
  749     throw std::logic_error(
"don't compare tokens with characters");
 
  759     return !(lhs == rhs);
 
  783         for(
const std::string& fname : 
files_) {
 
  784             std::size_t cpos = fname.rfind(
" ");
 
  786             if(cpos != std::string::npos && cpos >= symbol_index) {
 
  787                 std::stringstream ss;
 
  789                 << 
"' in included directory '" << name << 
"'.\nThe included symbol probably looks similar to '" 
  812     if(!file_stream->good()) {
 
  824         const std::string& history,
 
  825         const std::string& name,
 
  827         const std::string& directory,
 
  828         const std::string& domain,
 
  829         std::unique_ptr<std::map<std::string, std::string>> defines,
 
  832     , in_scope_(std::move(
i))
 
  834     , directory_(directory)
 
  836     , local_defines_(std::move(defines))
 
  841     , is_define_(is_define)
 
  843     std::ostringstream 
s;
 
  847         if(!history.empty()) {
 
  854     if(!
t.location_.empty()) {
 
  855         s << 
' ' << 
t.linenum_ << 
' ' << 
t.location_;
 
  858     t.location_ = 
s.str();
 
  859     t.linenum_ = linenum;
 
  863     if(
t.textdomain_ != domain) {
 
  865         t.textdomain_ = domain;
 
  892     std::ostringstream 
s;
 
  904     unsigned stack_pos = 
tokens_.back().stack_pos;
 
  918         assert(stack_pos == 
strings_.size());
 
  934         assert(stack_pos + 1 == 
strings_.size());
 
  948         if(
in_.
eof() || (
c != 
' ' && 
c != 
'\t')) {
 
  985         res += 
static_cast<char>(
c);
 
 1006             res += 
static_cast<char>(
c);
 
 1019             res += 
static_cast<char>(
c);
 
 1082     char c = 
static_cast<char>(
in_.
get());
 
 1097             s = 
"#ifdef or #ifndef";
 
 1100             s = 
"Quoted string";
 
 1103             s = 
"Verbatim string";
 
 1107             s = 
"Macro substitution";
 
 1110             s = 
"Macro argument";
 
 1124         std::string buffer(1, 
c);
 
 1127             char d = 
static_cast<char>(
in_.
get());
 
 1142         if(
c == 
'>' && 
in_.
peek() == 
'>') {
 
 1146     } 
else if(
c == 
'<' && 
in_.
peek() == 
'<') {
 
 1151     } 
else if(
c == 
'"') {
 
 1163     } 
else if(
c == 
'{') {
 
 1170         bool comment = 
false;
 
 1172         if(command == 
"define") {
 
 1176             std::map<std::string, std::string> optargs;
 
 1179                 parent_.
error(
"No macro name found after #define directive", linenum);
 
 1182             std::string symbol = items.front();
 
 1183             items.erase(items.begin());
 
 1184             int found_arg = 0, found_enddef = 0, found_deprecate = 0;
 
 1185             utils::optional<DEP_LEVEL> deprecation_level;
 
 1186             std::string buffer, deprecation_detail;
 
 1191                 char d = 
static_cast<char>(
in_.
get());
 
 1198                     } 
else if(
in_.
peek() == 
'd') {
 
 1199                         found_deprecate = 1;
 
 1204                     if(found_arg > 0 && ++found_arg == 4) {
 
 1205                         if(std::equal(buffer.end() - 3, buffer.end(), 
"arg")) {
 
 1206                             buffer.erase(buffer.end() - 4, buffer.end());
 
 1212                             std::string argbuffer;
 
 1214                             int found_endarg = 0;
 
 1220                                 char e = 
static_cast<char>(
in_.
get());
 
 1229                                 } 
else if(found_endarg > 0 && ++found_endarg == 7) {
 
 1230                                     if(std::equal(argbuffer.end() - 6, argbuffer.end(), 
"endarg")) {
 
 1231                                         argbuffer.erase(argbuffer.end() - 7, argbuffer.end());
 
 1232                                         optargs[argname] = argbuffer;
 
 1243                     if(found_deprecate > 0 && ++found_deprecate == 11) {
 
 1244                         if(std::equal(buffer.end() - 10, buffer.end(), 
"deprecated")) {
 
 1245                             buffer.erase(buffer.end() - 11, buffer.end());
 
 1249                                 if(deprecation_level) {
 
 1250                                     deprecation_level = std::max(*deprecation_level, 
level);
 
 1252                                     deprecation_level = 
level;
 
 1254                             } 
catch(
const std::invalid_argument&) {
 
 1264                             if(!deprecation_detail.empty()){
 
 1265                                 deprecation_detail += 
'\n';
 
 1272                     if(found_enddef > 0 && ++found_enddef == 7) {
 
 1273                         if(std::equal(buffer.end() - 6, buffer.end(), 
"enddef")) {
 
 1277                             if(std::equal(buffer.end() - 6, buffer.end(), 
"define")) { 
 
 1281                                         "Preprocessor error: #define is not allowed inside a #define/#enddef pair",
 
 1289             if(found_enddef != 7) {
 
 1296                     std::ostringstream new_pos, old_pos;
 
 1302                     WRN_PREPROC << 
"Redefining macro " << symbol << 
" without explicit #undef at " 
 1304                                 << 
"previously defined at " << 
lineno_string(old_pos.str());
 
 1307                 buffer.erase(buffer.end() - 7, buffer.end());
 
 1310                         deprecation_detail, deprecation_level, deprecation_version));
 
 1314         } 
else if(command == 
"ifdef" || command == 
"ifndef") {
 
 1315             const bool negate = command[2] == 
'n';
 
 1317             const std::string& symbol = 
read_word();
 
 1318             if(symbol.empty()) {
 
 1322             DBG_PREPROC << 
"testing for macro " << symbol << 
": " << (found ? 
"defined" : 
"not defined");
 
 1324         } 
else if(command == 
"ifhave" || command == 
"ifnhave") {
 
 1325             const bool negate = command[2] == 
'n';
 
 1327             const std::string& symbol = 
read_word();
 
 1328             if(symbol.empty()) {
 
 1332             DBG_PREPROC << 
"testing for file or directory " << symbol << 
": " << (found ? 
"found" : 
"not found");
 
 1334         } 
else if(command == 
"ifver" || command == 
"ifnver") {
 
 1335             const bool negate = command[2] == 
'n';
 
 1338             const std::string& vsymstr = 
read_word();
 
 1340             const std::string& vopstr = 
read_word();
 
 1342             const std::string& vverstr = 
read_word();
 
 1352                     parent_.
error(
"First argument macro in #ifver/#ifnver should not require arguments", 
linenum_);
 
 1359                 DBG_PREPROC << 
"testing version '" << version1.
str() << 
"' against '" << version2.
str() << 
"' (" 
 1360                             << vopstr << 
"): " << (found ? 
"match" : 
"no match");
 
 1364                 std::string 
err = 
"Undefined macro in #ifver/#ifnver first argument: '";
 
 1369         } 
else if(command == 
"else") {
 
 1381         } 
else if(command == 
"endif") {
 
 1393         } 
else if(command == 
"textdomain") {
 
 1397                 put(
"#textdomain ");
 
 1402         } 
else if(command == 
"enddef") {
 
 1404         } 
else if(command == 
"undef") {
 
 1406             const std::string& symbol = 
read_word();
 
 1411         } 
else if(command == 
"error") {
 
 1414                 std::ostringstream error;
 
 1419         } 
else if(command == 
"warning") {
 
 1422                 std::ostringstream warning;
 
 1428         } 
else if(command == 
"deprecated") {
 
 1434             } 
catch(
const std::invalid_argument&) {
 
 1461         } 
else if(
c == 
'}') {
 
 1480             if(
strings_.size() <= 
static_cast<std::size_t
>(
token.stack_pos)) {
 
 1485             std::string::size_type pos;
 
 1488                 symbol.erase(
b + pos, 
b + symbol.find(
'\n', pos + 1) + 1);
 
 1491             std::map<std::string, std::string>::const_iterator arg;
 
 1492             preproc_map::const_iterator macro;
 
 1510                     std::ostringstream error;
 
 1511                     error << 
"Macro argument '" << symbol << 
"' does not expect any arguments";
 
 1515                 std::ostringstream v;
 
 1524                 std::size_t optional_arg_num = 0;
 
 1526                 auto defines = std::make_unique<std::map<std::string, std::string>>();
 
 1533                 for(std::size_t 
i = 0; 
i < nb_arg; ++
i) {
 
 1542                         std::size_t equals_pos = str.find_first_of(
"=");
 
 1544                         if(equals_pos != std::string::npos) {
 
 1545                             std::size_t argname_pos = str.substr(0, equals_pos).find_last_of(
" \n") + 1;
 
 1547                             std::string argname = str.substr(argname_pos, equals_pos - argname_pos);
 
 1550                                 (*defines)[argname] = str.substr(equals_pos + 1);
 
 1554                                 DBG_PREPROC << 
"Found override for " << argname << 
" in call to macro " << symbol;
 
 1556                                 std::ostringstream warning;
 
 1557                                 warning << 
"Unrecognized optional argument passed to macro '" << symbol << 
"': '" 
 1570                         if(defines->find(argument.first) == defines->end()) {
 
 1574                             std::istream 
in(buf.get());
 
 1578                             auto temp_defines = std::make_unique<std::map<std::string, std::string>>();
 
 1579                             temp_defines->insert(defines->begin(), defines->end());
 
 1584                             std::ostringstream res;
 
 1587                             DBG_PREPROC << 
"Setting default for optional argument " << argument.first << 
" in macro " 
 1590                             (*defines)[argument.first] = res.str();
 
 1595                 if(nb_arg - optional_arg_num != val.
arguments.size()) {
 
 1597                     const std::string 
filename = locations.empty() ? 
"<command-line>" : 
get_filename(locations[0]);
 
 1598                     std::ostringstream error;
 
 1599                     error << 
"Preprocessor symbol '" << symbol << 
"' defined at " << 
filename << 
":" 
 1600                           << val.
linenum << 
" expects " << val.
arguments.size() << 
" arguments, but has " 
 1601                           << nb_arg - optional_arg_num << 
" arguments";
 
 1615                     DBG_PREPROC << 
"substituting (slow) macro " << symbol;
 
 1623                     std::ostringstream res;
 
 1625                         std::istream 
in(buf.get());
 
 1635                 LOG_PREPROC << 
"Macro definition not found for " << symbol << 
", attempting to open as file.";
 
 1646                         std::ostringstream res;
 
 1648                             std::istream 
in(buf.get());
 
 1649                             buf->add_preprocessor<
preprocessor_file>(nfname.value(), nfname->size() - symbol.size());
 
 1657                     std::ostringstream error;
 
 1658                     error << 
"Macro/file '" << symbol << 
"' is missing";
 
 1666                 std::ostringstream 
s;
 
 1691         : std::istream(nullptr)
 
 1698         : std::istream(nullptr)
 
 1706         clear(std::ios_base::goodbit);
 
 1707         exceptions(std::ios_base::goodbit);
 
 1711     template<
typename Preprocessor, 
typename... Args>
 
 1735     log_scope(
"preprocessing file " + fname + 
" ...");
 
 1737     auto stream = std::make_unique<preprocessor_istream>(defines);
 
 1744     log_scope(
"preprocessing file " + fname + 
" ...");
 
 1746     auto stream = std::make_unique<preprocessor_istream>();
 
 1752     const std::string& contents, 
const std::string& textdomain)
 
 1754     log_scope(
"preprocessing string " + contents.substr(0, 10) + 
" ...");
 
 1756     auto stream = std::make_unique<preprocessor_istream>();
 
 1758         std::make_unique<std::istringstream>(contents), 
"<string>", 
"", 1, 
game_config::path, textdomain, 
nullptr);
 
 1764     const std::string& contents, 
const std::string& textdomain, 
preproc_map& defines)
 
 1766     log_scope(
"preprocessing string " + contents.substr(0, 10) + 
" ...");
 
 1768     auto stream = std::make_unique<preprocessor_istream>(defines);
 
 1770         std::make_unique<std::istringstream>(contents), 
"<string>", 
"", 1, 
game_config::path, textdomain, 
nullptr);
 
 1778         bool write_plain_cfg,
 
 1779         const std::string& parent_directory)
 
 1782         std::vector<std::string> dirs, files;
 
 1788         for(
const std::string& dir : dirs) {
 
 1794         for(
const std::string& file : files) {
 
 1803     LOG_PREPROC << 
"processing resource: " << res_name;
 
 1812     std::stringstream ss;
 
 1818     ss.exceptions(std::ios_base::failbit);
 
 1820     ss << (*stream).rdbuf();
 
 1824     if(write_cfg || write_plain_cfg) {
 
 1825         std::string streamContent = ss.str();
 
 1832             LOG_PREPROC << 
"writing cfg file: " << preproc_res_name;
 
 1839         if(write_plain_cfg) {
 
 1840             LOG_PREPROC << 
"writing plain cfg file: " << (preproc_res_name + 
".plain");
 
unsigned in
If equal to search_counter, the node is off the list.
Helper class for buffering a std::istream.
Helper class for buffering a std::istream.
bool eof() const
Is the end of input reached?
int get()
Gets and consumes a character from the buffer.
int peek()
Gets a character from the buffer.
Class for writing a config out to a file in pieces.
void close_child(const std::string &key)
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.
void open_child(const std::string &key)
A config object defines a single node in a WML file, with access to child nodes.
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
child_itors child_range(std::string_view key)
bool has_attribute(std::string_view key) const
Specialized preprocessor for handling any kind of input stream.
std::unique_ptr< std::map< std::string, std::string > > local_defines_
Mapping of macro arguments to their content.
int skipping_
Non-zero when the preprocessor has to skip some input text.
virtual preprocessor::MODE parse_mode() override
Returns the appropriate parsing mode for this preprocessor.
void conditional_skip(bool skip)
int slowpath_
Set to true whenever input tokens cannot be directly sent to the target buffer.
std::string read_rest_of_line()
friend bool operator!=(preprocessor_data::token_desc::token_type, char)
std::vector< std::string > strings_
Buffer for delayed input processing.
filesystem::scoped_istream in_scope_
Manages the lifetime of the std::istream pointer we own.
preprocessor_data(preprocessor_streambuf &, filesystem::scoped_istream, const std::string &history, const std::string &name, int line, const std::string &dir, const std::string &domain, std::unique_ptr< std::map< std::string, std::string >> defines, bool is_define=false)
buffered_istream in_
Input stream.
friend bool operator==(preprocessor_data::token_desc::token_type, char)
bool is_define_
True iff we are currently parsing a macros content, otherwise false.
std::vector< token_desc > tokens_
Stack of nested preprocessing chunks.
virtual bool get_chunk() override
Preprocesses and sends some text to the parent_ buffer.
void push_token(token_desc::token_type)
Specialized preprocessor for handling a file or a set of files.
std::vector< std::string >::const_iterator pos_
std::vector< std::string > files_
virtual bool get_chunk() override
Inserts and processes the next file in the list of included files.
const std::string & name_
std::vector< std::string >::const_iterator end_
virtual void init() override
Allows specifying any actions that need to be called after the constructor completes.
preprocessor_file(preprocessor_streambuf &t, const std::string &name, std::size_t symbol_index=-1)
Constructor.
preprocessor_istream(preproc_map &defines)
void process(Args &&... args)
preproc_map owned_defines_
Defines map to be used if no external map is given.
preprocessor_streambuf buffer_
The underlying processing buffer.
Target for sending preprocessed output.
std::stringstream buffer_
Buffer filled by the current preprocessor.
preprocessor * current() const
bool quoted_
Set to true if one preprocessor for this target started to read a string.
virtual int underflow() override
Inherited from basic_streambuf.
void restore_old_preprocessor()
Restores the old preprocessing context.
std::string out_buffer_
Buffer read by the STL stream.
std::string get_current_file()
Decodes the filenames placed in a location.
preprocessor_streambuf(preproc_map &def)
std::deque< std::unique_ptr< preprocessor > > preprocessor_queue_
Input preprocessor queue.
void add_preprocessor(A &&... args)
void error(const std::string &, int)
preprocessor_streambuf(const preprocessor_streambuf &t)
void warning(const std::string &, int)
Base class for preprocessing an input.
virtual MODE parse_mode()
Returns the appropriate parsing mode for this preprocessor.
std::string old_textdomain_
virtual void init()
Allows specifying any actions that need to be called after the constructor completes.
std::string old_location_
friend class preprocessor_streambuf
preprocessor(preprocessor_streambuf &t)
Sets up a new preprocessor for stream buffer t.
virtual bool get_chunk()=0
Preprocesses and sends some text to the parent_ buffer.
preprocessor_streambuf & parent_
Represents version numbers.
std::string str() const
Serializes the version number into string form.
Definitions for the interface to Wesnoth Markup Language (WML).
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
DEP_LEVEL
See https://wiki.wesnoth.org/CompatibilityStandards for more info.
bool do_version_check(const version_info &a, VERSION_COMP_OP op, const version_info &b)
VERSION_COMP_OP parse_version_op(const std::string &op_str)
Interfaces for manipulating version numbers of engine, add-ons, etc.
Standard logging facilities (interface).
#define log_scope(description)
void clear()
Clear the current render target.
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs, name_mode mode, filter_mode filter, reorder_mode reorder, file_tree_checksum *checksum)
Get a list of all files and/or directories in a given directory.
bool is_cfg(const std::string &filename)
Returns true if the file ends with the wmlfile extension.
std::string base_name(const std::string &file, const bool remove_extension)
Returns the base filename of a file, with directory name stripped.
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > ¤t_dir)
Returns a translated path to the actual file or directory, if it exists.
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
static bool create_directory_if_missing_recursive(const bfs::path &dirpath)
std::unique_ptr< std::istream > scoped_istream
void write_file(const std::string &fname, const std::string &data, std::ios_base::openmode mode)
Throws io_exception if an error occurs.
std::string get_short_wml_path(const std::string &filename)
Returns a short path to filename, skipping the (user) data directory.
std::string directory_name(const std::string &file)
Returns the directory name of a file, with filename stripped.
const version_info wesnoth_version(VERSION)
config read(std::istream &in, abstract_validator *validator)
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
std::vector< std::string > quoted_split(const std::string &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...
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
bool portable_isspace(const char c)
std::string escape(std::string_view str, const char *special_chars)
Prepends a configurable set of characters with a backslash.
std::vector< std::string > split(const config_attribute_value &val)
std::string::const_iterator iterator
static lg::log_domain log_preprocessor("preprocessor")
filesystem::scoped_istream preprocess_string(const std::string &contents, const std::string &textdomain)
static const std::string left_curly_str
static const std::string current_dir_str
std::string lineno_string(const std::string &lineno)
void preprocess_resource(const std::string &res_name, preproc_map *defines_map, bool write_cfg, bool write_plain_cfg, const std::string &parent_directory)
static std::string get_file_code(const std::string &filename)
static bool encode_filename
static const std::string current_file_str
bool operator==(preprocessor_data::token_desc::token_type, char)
static std::string get_filename(const std::string &file_code)
static std::string get_location(const std::string &loc)
std::ostream & operator<<(std::ostream &stream, const preproc_define &def)
static std::map< std::string, int > file_number_map
static const std::string right_curly_str
bool operator!=(preprocessor_data::token_desc::token_type rhs, char lhs)
filesystem::scoped_istream preprocess_file(const std::string &fname, preproc_map &defines)
Function to use the WML preprocessor on a file.
static std::string preprocessor_error_detail_prefix
std::map< std::string, struct preproc_define > preproc_map
constexpr unsigned char INLINED_PREPROCESS_DIRECTIVE_CHAR
std::string filename
Filename.
void write(config_writer &, const std::string &) const
version_info deprecation_version
bool operator<(const preproc_define &) const
void write_argument(config_writer &, const std::string &) const
void read(const config &)
void read_argument(const config &)
std::string deprecation_message
std::vector< std::string > arguments
static void insert(preproc_map &, const config &)
std::map< std::string, std::string > optional_arguments
bool operator==(const preproc_define &) const
utils::optional< DEP_LEVEL > deprecation_level
bool is_deprecated() const
Description of a preprocessing chunk.
token_desc(token_type type, const int stack_pos, const int linenum)
int stack_pos
Starting position in strings_ of the delayed text for this chunk.
contains the current text being parsed as well as the token_type of what's being parsed.
static map_location::direction n
static map_location::direction s
Some defines: VERSION, PACKAGE, MIN_SAVEGAME_VERSION.