00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "../global.hpp"
00020
00021 #include "../filesystem.hpp"
00022 #include "../log.hpp"
00023 #include "../wesconfig.h"
00024 #include "preprocessor.hpp"
00025 #include "string_utils.hpp"
00026
00027 #include <algorithm>
00028 #include <cassert>
00029 #include <iostream>
00030 #include <sstream>
00031 #include <vector>
00032
00033 #define ERR_CF LOG_STREAM(err, config)
00034 #define LOG_CF LOG_STREAM(info, config)
00035 #define DBG_CF LOG_STREAM(debug, config)
00036
00037 using std::streambuf;
00038
00039 bool preproc_define::operator==(preproc_define const &v) const {
00040 return value == v.value && arguments == v.arguments;
00041 }
00042
00043 class preprocessor;
00044 class preprocessor_file;
00045 class preprocessor_data;
00046 class preprocessor_streambuf;
00047 struct preprocessor_deleter;
00048
00049 class preprocessor
00050 {
00051 preprocessor *const old_preprocessor_;
00052 std::string old_textdomain_;
00053 std::string old_location_;
00054 int old_linenum_;
00055 protected:
00056 preprocessor_streambuf &target_;
00057 std::vector<std::string> *called_macros_;
00058 preprocessor(preprocessor_streambuf &, std::vector<std::string> *);
00059 public:
00060 virtual bool get_chunk() = 0;
00061 virtual ~preprocessor();
00062 };
00063
00064 class preprocessor_streambuf: public streambuf
00065 {
00066 std::string out_buffer_;
00067 virtual int underflow();
00068 std::ostringstream buffer_;
00069 preprocessor *current_;
00070 preproc_map *defines_;
00071 preproc_map default_defines_;
00072 std::string textdomain_;
00073 std::string location_;
00074 std::string *error_log;
00075 int linenum_;
00076 int depth_;
00077 int buffer_size_;
00078 bool quoted_;
00079 friend class preprocessor;
00080 friend class preprocessor_file;
00081 friend class preprocessor_data;
00082 friend struct preprocessor_deleter;
00083 preprocessor_streambuf(preprocessor_streambuf const &);
00084 public:
00085 preprocessor_streambuf(preproc_map *, std::string *);
00086 std::string lineno_string(std::string const &);
00087 void error(const std::string &, const std::string &);
00088 };
00089
00090 preprocessor_streambuf::preprocessor_streambuf(preproc_map *def, std::string *err_log) :
00091 streambuf(),
00092 out_buffer_(""),
00093 buffer_(),
00094 current_(NULL),
00095 defines_(def),
00096 default_defines_(),
00097 textdomain_(PACKAGE),
00098 location_(""),
00099 error_log(err_log),
00100 linenum_(0),
00101 depth_(0),
00102 buffer_size_(0),
00103 quoted_(false)
00104 {
00105 }
00106
00107 preprocessor_streambuf::preprocessor_streambuf(preprocessor_streambuf const &t) :
00108 streambuf(),
00109 out_buffer_(""),
00110 buffer_(),
00111 current_(NULL),
00112 defines_(t.defines_),
00113 default_defines_(),
00114 textdomain_(PACKAGE),
00115 location_(""),
00116 error_log(t.error_log),
00117 linenum_(0),
00118 depth_(t.depth_),
00119 buffer_size_(0),
00120 quoted_(t.quoted_)
00121 {
00122 }
00123
00124
00125
00126 int preprocessor_streambuf::underflow()
00127 {
00128 unsigned sz = 0;
00129 if (char *gp = gptr()) {
00130 if (gp < egptr()) {
00131
00132
00133 return *gp;
00134 }
00135
00136
00137 sz = out_buffer_.size();
00138 buffer_.str(std::string());
00139 if (sz > 3) {
00140 buffer_ << out_buffer_.substr(sz - 3);
00141 sz = 3;
00142 }
00143 else
00144 {
00145 buffer_ << out_buffer_;
00146 }
00147 buffer_size_ = sz;
00148 } else {
00149
00150 }
00151 const int desired_fill_amount = 2000;
00152 while (current_ && buffer_size_ < desired_fill_amount) {
00153
00154 if (!current_->get_chunk()) {
00155
00156 delete current_;
00157 }
00158 }
00159
00160 out_buffer_ = buffer_.str();
00161 char *begin = &*out_buffer_.begin();
00162 unsigned bs = out_buffer_.size();
00163 setg(begin, begin + sz, begin + bs);
00164 if (sz >= bs)
00165 return EOF;
00166 return static_cast<unsigned char>(*(begin + sz));
00167 }
00168
00169 std::string preprocessor_streambuf::lineno_string(std::string const &lineno)
00170 {
00171 std::vector< std::string > pos = utils::quoted_split(lineno, ' ');
00172 std::vector< std::string >::const_iterator i = pos.begin(), end = pos.end();
00173 std::string included_from = " included from ";
00174 std::string res;
00175 while (i != end) {
00176 std::string const &line = *(i++);
00177 std::string const &file = i != end ? *(i++) : "<unknown>";
00178 if (!res.empty())
00179 res += included_from;
00180 res += file + ':' + line;
00181 }
00182 if (res.empty()) res = "???";
00183 return res;
00184 }
00185
00186 void preprocessor_streambuf::error(const std::string& error_type, const std::string &pos)
00187 {
00188 utils::string_map i18n_symbols;
00189 std::string position, error;
00190 position = lineno_string(pos);
00191 error = error_type + " at " + position;
00192 ERR_CF << error << '\n';
00193 if(error_log!=NULL)
00194 *error_log += error + '\n';
00195
00196 throw preproc_config::error(error);
00197 }
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209 preprocessor::preprocessor(preprocessor_streambuf &t, std::vector<std::string> *callstack) :
00210 old_preprocessor_(t.current_),
00211 old_textdomain_(t.textdomain_),
00212 old_location_(t.location_),
00213 old_linenum_(t.linenum_),
00214 target_(t),
00215 called_macros_(callstack)
00216 {
00217 ++target_.depth_;
00218 target_.current_ = this;
00219 }
00220
00221 namespace {
00222 void count_extra_digits(int const& line_number, int& buffer_size) {
00223 for(int digit_mark = 10; line_number >= digit_mark; digit_mark *= 10) {
00224
00225 ++buffer_size;
00226 }
00227 }
00228 }
00229
00230 preprocessor::~preprocessor()
00231 {
00232 assert(target_.current_ == this);
00233 target_.current_ = old_preprocessor_;
00234 target_.location_ = old_location_;
00235 target_.linenum_ = old_linenum_;
00236 target_.textdomain_ = old_textdomain_;
00237 if (!old_location_.empty()) {
00238 target_.buffer_ << "\376line " << old_linenum_ << ' ' << old_location_ << '\n';
00239 const int fixed_char_count = 9;
00240 count_extra_digits(old_linenum_, target_.buffer_size_);
00241 target_.buffer_size_ += old_location_.size() + fixed_char_count;
00242 }
00243 if (!old_textdomain_.empty()) {
00244 target_.buffer_ << "\376textdomain " << old_textdomain_ << '\n';
00245 const int fixed_char_count = 13;
00246 target_.buffer_size_ += old_textdomain_.size() + fixed_char_count;
00247 }
00248 --target_.depth_;
00249 }
00250
00251
00252
00253
00254
00255
00256
00257 class preprocessor_file: preprocessor
00258 {
00259 std::vector< std::string > files_;
00260 std::vector< std::string >::const_iterator pos_, end_;
00261 public:
00262 preprocessor_file(preprocessor_streambuf &, std::vector<std::string> *, std::string const &);
00263 virtual bool get_chunk();
00264 };
00265
00266 class preprocessor_data: preprocessor
00267 {
00268 struct token_desc
00269 {
00270
00271 char type;
00272 int stack_pos;
00273 int linenum;
00274 };
00275 scoped_istream in_;
00276 std::string directory_;
00277 std::vector< std::string > strings_;
00278 std::vector< token_desc > tokens_;
00279 bool is_macro;
00280 int slowpath_,
00281 skipping_,
00282 linenum_;
00283
00284 std::string read_word();
00285 std::string read_line();
00286 void skip_spaces();
00287 void skip_eol();
00288 void push_token(char);
00289 void pop_token();
00290 void put(char);
00291 void put(std::string const &
00292 );
00293 public:
00294 preprocessor_data(preprocessor_streambuf &,
00295 std::vector<std::string> *,
00296 std::istream *,
00297 std::string const &history,
00298 std::string const &name, int line,
00299 std::string const &dir, std::string const &domain,
00300 std::string* = NULL);
00301 ~preprocessor_data();
00302 virtual bool get_chunk();
00303 };
00304
00305 preprocessor_file::preprocessor_file(preprocessor_streambuf &t, std::vector<std::string> *callstack,
00306 std::string const &name) :
00307 preprocessor(t,callstack),
00308 files_(),
00309 pos_(),
00310 end_()
00311 {
00312 if (is_directory(name))
00313 get_files_in_dir(name, &files_, NULL, ENTIRE_FILE_PATH, SKIP_MEDIA_DIR, DO_REORDER);
00314 else {
00315 std::istream * file_stream = istream_file(name);
00316 if (!file_stream->good()) {
00317 ERR_CF << "Could not open file " << name << "\n";
00318 delete file_stream;
00319 }
00320 else
00321 new preprocessor_data(t, called_macros_, file_stream, "", name, 1, directory_name(name), t.textdomain_);
00322 }
00323 pos_ = files_.begin();
00324 end_ = files_.end();
00325 }
00326
00327
00328
00329
00330
00331
00332
00333 bool preprocessor_file::get_chunk()
00334 {
00335 while (pos_ != end_) {
00336 const std::string &name = *(pos_++);
00337 unsigned sz = name.size();
00338
00339 if (sz < 5 || !std::equal(name.rbegin(), name.rbegin() + 4, "gfc."))
00340 continue;
00341 new preprocessor_file(target_, called_macros_, name);
00342 return true;
00343 }
00344 return false;
00345 }
00346
00347 preprocessor_data::preprocessor_data(preprocessor_streambuf &t, std::vector<std::string> *callstack,
00348 std::istream *i, std::string const &history, std::string const &name, int linenum,
00349 std::string const &directory, std::string const &domain, std::string *symbol) :
00350 preprocessor(t,callstack),
00351 in_(i),
00352 directory_(directory),
00353 strings_(),
00354 tokens_(),
00355 slowpath_(0),
00356 skipping_(0),
00357 linenum_(linenum)
00358 {
00359 if(symbol!=NULL)
00360 {
00361 is_macro=true;
00362 called_macros_->push_back(*symbol);
00363 }
00364 else
00365 {
00366 is_macro=false;
00367 }
00368
00369 std::ostringstream s;
00370
00371 s << history;
00372 if (!name.empty()) {
00373 std::string ename(name);
00374 if (!history.empty())
00375 s << ' ';
00376 s << utils::escape(ename, " \\");
00377 }
00378 if (!t.location_.empty())
00379 s << ' ' << t.linenum_ << ' ' << t.location_;
00380 t.location_ = s.str();
00381 t.linenum_ = linenum;
00382 t.textdomain_ = domain;
00383
00384 t.buffer_ << "\376line " << linenum
00385 << ' ' << t.location_ << "\n\376textdomain " << domain << '\n';
00386 const int fixed_char_count = 22;
00387 count_extra_digits(linenum, t.buffer_size_);
00388 t.buffer_size_ += t.location_.size() + domain.size() + fixed_char_count;
00389
00390 push_token('*');
00391 }
00392
00393 preprocessor_data::~preprocessor_data()
00394 {
00395 if(is_macro)
00396 {
00397 called_macros_->pop_back();
00398 }
00399 }
00400
00401 void preprocessor_data::push_token(char t)
00402 {
00403 token_desc token = { t, strings_.size(), linenum_ };
00404 tokens_.push_back(token);
00405 std::ostringstream s;
00406 if (!skipping_ && slowpath_) {
00407 s << "\376line " << linenum_ << ' ' << target_.location_
00408 << "\n\376textdomain " << target_.textdomain_ << '\n';
00409 }
00410 strings_.push_back(s.str());
00411 }
00412
00413 void preprocessor_data::pop_token()
00414 {
00415 strings_.erase(strings_.begin() + tokens_.back().stack_pos, strings_.end());
00416 tokens_.pop_back();
00417 }
00418
00419 void preprocessor_data::skip_spaces()
00420 {
00421 for(;;) {
00422 int c = in_->peek();
00423 if (!in_->good() || (c != ' ' && c != '\t'))
00424 return;
00425 in_->get();
00426 }
00427 }
00428
00429 void preprocessor_data::skip_eol()
00430 {
00431 for(;;) {
00432 int c = in_->get();
00433 if (c == '\n') {
00434 ++linenum_;
00435 return;
00436 }
00437 if (!in_->good())
00438 return;
00439 }
00440 }
00441
00442 std::string preprocessor_data::read_word()
00443 {
00444 std::string res;
00445 for(;;) {
00446 int c = in_->peek();
00447 if (c == preprocessor_streambuf::traits_type::eof() || utils::portable_isspace(c)) {
00448
00449 return res;
00450 }
00451 in_->get();
00452 res += static_cast<char>(c);
00453 }
00454 }
00455
00456 std::string preprocessor_data::read_line()
00457 {
00458 std::string res;
00459 for(;;) {
00460 int c = in_->get();
00461 if (c == '\n') {
00462 ++linenum_;
00463 return res;
00464 }
00465 if (!in_->good())
00466 return res;
00467 if (c != '\r')
00468 res += static_cast<char>(c);
00469 }
00470 }
00471
00472 void preprocessor_data::put(char c)
00473 {
00474 if (skipping_)
00475 return;
00476 if (slowpath_) {
00477 strings_.back() += c;
00478 return;
00479 }
00480 if ((linenum_ != target_.linenum_ && c != '\n') ||
00481 (linenum_ != target_.linenum_ + 1 && c == '\n')) {
00482 target_.linenum_ = linenum_;
00483 if (c == '\n')
00484 --target_.linenum_;
00485
00486 target_.buffer_ << "\376line " << target_.linenum_
00487 << ' ' << target_.location_ << '\n';
00488 const int fixed_char_count = 9;
00489 count_extra_digits(target_.linenum_, target_.buffer_size_);
00490 target_.buffer_size_ += target_.location_.size() + fixed_char_count;
00491 }
00492 if (c == '\n')
00493 ++target_.linenum_;
00494 target_.buffer_ << c;
00495 target_.buffer_size_ += 1;
00496 }
00497
00498 void preprocessor_data::put(std::string const &s )
00499 {
00500 if (skipping_)
00501 return;
00502 if (slowpath_) {
00503 strings_.back() += s;
00504 return;
00505 }
00506 target_.buffer_ << s;
00507
00508 target_.buffer_size_ += s.size();
00509 }
00510
00511 bool preprocessor_data::get_chunk()
00512 {
00513 char c = in_->get();
00514 token_desc &token = tokens_.back();
00515 if (!in_->good()) {
00516
00517
00518 char const *s;
00519 switch (token.type) {
00520 case '*': return false;
00521 case 'i':
00522 case 'I':
00523 case 'j':
00524 case 'J': s = "#ifdef or #ifndef"; break;
00525 case '"': s = "Quoted string"; break;
00526 case '[':
00527 case '{': s = "Macro substitution"; break;
00528 case '(': s = "Macro argument"; break;
00529 default: s = "???";
00530 }
00531 std::ostringstream error;
00532 error<<s<<" not terminated";
00533 std::ostringstream location;
00534 location<<linenum_<<' '<<target_.location_;
00535 pop_token();
00536 target_.error(error.str(), location.str());
00537 }
00538 if (c == '\n')
00539 ++linenum_;
00540 if (c == '\376') {
00541 std::string buffer(1, c);
00542 for(;;) {
00543 char d = in_->get();
00544 if (!in_->good() || d == '\n')
00545 break;
00546 buffer += d;
00547 }
00548 buffer += '\n';
00549
00550 put(buffer);
00551 } else if (c == '"') {
00552 if (token.type == '"') {
00553 target_.quoted_ = false;
00554 put(c);
00555 if (!skipping_ && slowpath_) {
00556 std::string tmp = strings_.back();
00557 pop_token();
00558 strings_.back() += tmp;
00559 } else
00560 pop_token();
00561 } else if (!target_.quoted_) {
00562 target_.quoted_ = true;
00563 if (token.type == '{')
00564 token.type = '[';
00565 push_token('"');
00566 put(c);
00567 } else {
00568 std::string error="Nested quoted string";
00569 std::ostringstream location;
00570 location<<linenum_<<' '<<target_.location_;
00571 target_.error(error, location.str());
00572 }
00573 } else if (c == '{') {
00574 if (token.type == '{')
00575 token.type = '[';
00576 push_token('{');
00577 ++slowpath_;
00578 } else if (c == ')' && token.type == '(') {
00579 tokens_.pop_back();
00580 strings_.push_back(std::string());
00581 } else if (c == '#' && !target_.quoted_) {
00582 std::string command = read_word();
00583 bool comment = false;
00584 if (command == "define") {
00585 skip_spaces();
00586 int linenum = linenum_;
00587 std::vector< std::string > items = utils::split(read_line(), ' ');
00588 if (items.empty()) {
00589 std::string error="No macro name found after #define directive";
00590 std::ostringstream location;
00591 location<<linenum_<<' '<<target_.location_;
00592 target_.error(error, location.str());
00593 }
00594 std::string symbol = items.front();
00595 items.erase(items.begin());
00596 int found_enddef = 0;
00597 std::string buffer;
00598 for(;;) {
00599 if (!in_->good())
00600 break;
00601 char d = in_->get();
00602 if (d == '\n')
00603 ++linenum_;
00604 buffer += d;
00605 if (d == '#')
00606 found_enddef = 1;
00607 else if (found_enddef > 0)
00608 if (++found_enddef == 7) {
00609 if (std::equal(buffer.end() - 6, buffer.end(), "enddef"))
00610 break;
00611 else
00612 found_enddef = 0;
00613 }
00614 }
00615 if (found_enddef != 7) {
00616 std::string error="Unterminated preprocessor definition at";
00617 std::ostringstream location;
00618 location<<linenum_<<' '<<target_.location_;
00619 target_.error(error, location.str());
00620 }
00621 if (!skipping_) {
00622 buffer.erase(buffer.end() - 7, buffer.end());
00623 target_.defines_->insert(std::make_pair(
00624 symbol, preproc_define(buffer, items, target_.textdomain_,
00625 linenum + 1, target_.location_)));
00626 LOG_CF << "defining macro " << symbol << " (location " << target_.location_ << ")\n";
00627 }
00628 } else if (command == "ifdef") {
00629 skip_spaces();
00630 std::string const &symbol = read_word();
00631 bool skip = target_.defines_->count(symbol) == 0;
00632 DBG_CF << "testing for macro " << symbol << ": " << (skip ? "not defined" : "defined") << '\n';
00633 if (skip)
00634 ++skipping_;
00635 push_token(skip ? 'J' : 'i');
00636 } else if (command == "ifndef") {
00637 skip_spaces();
00638 std::string const &symbol = read_word();
00639 bool skip = target_.defines_->count(symbol) != 0;
00640 DBG_CF << "testing for macro " << symbol << ": " << (skip ? "not defined" : "defined") << '\n';
00641 if (skip)
00642 ++skipping_;
00643 push_token(skip ? 'J' : 'i');
00644 } else if (command == "else") {
00645 if (token.type == 'J') {
00646 pop_token();
00647 --skipping_;
00648 push_token('j');
00649 } else if (token.type == 'i') {
00650 pop_token();
00651 ++skipping_;
00652 push_token('I');
00653 } else {
00654 std::string error="Unexpected #else at";
00655 std::ostringstream location;
00656 location<<linenum_<<' '<<target_.location_;
00657 target_.error(error, location.str());
00658 }
00659 } else if (command == "endif") {
00660 switch (token.type) {
00661 case 'I':
00662 case 'J': --skipping_;
00663 case 'i':
00664 case 'j': break;
00665 default:
00666 std::string error="Unexpected #endif at";
00667 std::ostringstream location;
00668 location<<linenum_<<' '<<target_.location_;
00669 target_.error(error, location.str());
00670 }
00671 pop_token();
00672 } else if (command == "textdomain") {
00673 skip_spaces();
00674 std::string const &s = read_word();
00675 put("#textdomain ");
00676 put(s);
00677 target_.textdomain_ = s;
00678 comment = true;
00679 } else if (command == "enddef") {
00680 std::string error="Unexpected #enddef at";
00681 std::ostringstream location;
00682 location<<linenum_<<' '<<target_.location_;
00683 target_.error(error, location.str());
00684 } else if (command == "undef") {
00685 skip_spaces();
00686 std::string const &symbol = read_word();
00687 target_.defines_->erase(symbol);
00688 LOG_CF << "undefine macro " << symbol << " (location " << target_.location_ << ")\n";
00689 } else
00690 comment = true;
00691 skip_eol();
00692 if (comment)
00693 put('\n');
00694 } else if (token.type == '{' || token.type == '[') {
00695 if (c == '(') {
00696 if (token.type == '[')
00697 token.type = '{';
00698 else
00699 strings_.pop_back();
00700 push_token('(');
00701 } else if (utils::portable_isspace(c)) {
00702 if (token.type == '[') {
00703 strings_.push_back(std::string());
00704 token.type = '{';
00705 }
00706 } else if (c == '}') {
00707 --slowpath_;
00708 if (skipping_) {
00709 pop_token();
00710 return true;
00711 }
00712 if (token.type == '{') {
00713 if (!strings_.back().empty()) {
00714 std::ostringstream error;
00715 std::ostringstream location;
00716 error << "Can't parse new macro parameter with a macro call scope open";
00717 location<<linenum_<<' '<<target_.location_;
00718 target_.error(error.str(), location.str());
00719 }
00720 strings_.pop_back();
00721 }
00722
00723 std::string symbol = strings_[token.stack_pos];
00724 std::string::size_type pos;
00725 while ((pos = symbol.find('\376')) != std::string::npos) {
00726 std::string::iterator b = symbol.begin();
00727 symbol.erase(b + pos, b + symbol.find('\n', pos + 1) + 1);
00728 }
00729
00730
00731 preproc_map::const_iterator macro = target_.defines_->find(symbol);
00732 if(macro != target_.defines_->end()) {
00733
00734
00735
00736
00737 #if 0
00738
00739 if(symbol != "INCLUDE") {
00740 for(std::vector<std::string>::iterator
00741 iter=called_macros_->begin();
00742 iter!=called_macros_->end(); ++iter) {
00743 if(*iter==symbol) {
00744 std::ostringstream error;
00745 error << "symbol '" << symbol << "' will cause a recursive macro call";
00746 std::ostringstream location;
00747 location<<linenum_<<' '<<target_.location_;
00748 target_.error(error.str(), location.str());
00749 }
00750 }
00751 }
00752 #endif
00753 preproc_define const &val = macro->second;
00754 size_t nb_arg = strings_.size() - token.stack_pos - 1;
00755 if (nb_arg != val.arguments.size()) {
00756 std::ostringstream error;
00757 error << "preprocessor symbol '" << symbol << "' expects "
00758 << val.arguments.size() << " arguments, but has "
00759 << nb_arg << " arguments";
00760 std::ostringstream location;
00761 location<<linenum_<<' '<<target_.location_;
00762 target_.error(error.str(), location.str());
00763 }
00764
00765 std::stringstream *buffer = new std::stringstream;
00766 std::string::const_iterator i_bra = val.value.end();
00767 int macro_num = val.linenum;
00768 std::string macro_textdomain = val.textdomain;
00769 bool quoting = false;
00770 for(std::string::const_iterator i = val.value.begin(),
00771 i_end = val.value.end(); i != i_end; ++i) {
00772 char c = *i;
00773 if (c == '\n') {
00774 ++macro_num;
00775 } else if (c == '"') {
00776 quoting = !quoting;
00777 }
00778
00779 if (c == '{') {
00780 if (i_bra != i_end)
00781 buffer->write(&*i_bra - 1, i - i_bra + 1);
00782 i_bra = i + 1;
00783 } else if (i_bra == i_end) {
00784 if (c == '#' && !quoting) {
00785
00786
00787
00788 std::string::const_iterator i_beg = i + 1;
00789 if (i_end - i_beg >= 13 &&
00790 std::equal(i_beg, i_beg + 10, "textdomain")) {
00791 i_beg += 10;
00792 i = std::find(i_beg, i_end, '\n');
00793 if (i_beg != i)
00794 ++i_beg;
00795 macro_textdomain = std::string(i_beg, i);
00796 *buffer << "#textdomain " << macro_textdomain;
00797 ++macro_num;
00798 c = '\n';
00799 } else if((i_end - i_beg < 6 || (!std::equal(i_beg, i_beg + 6, "define")
00800 && !std::equal(i_beg, i_beg + 6, "ifndef")))
00801 && (i_end - i_beg < 5 || (!std::equal(i_beg, i_beg + 5, "ifdef")
00802 && !std::equal(i_beg, i_beg + 5, "endif") && !std::equal(i_beg, i_beg + 5, "undef")))
00803 && (i_end - i_beg < 4 || !std::equal(i_beg, i_beg + 4, "else"))) {
00804
00805
00806 i = std::find(i_beg, i_end, '\n');
00807 ++macro_num;
00808 c = '\n';
00809 }
00810 }
00811 buffer->put(c);
00812 } else if (c == '}') {
00813 size_t sz = i - i_bra;
00814 for(size_t n = 0; n < nb_arg; ++n) {
00815 std::string const &arg = val.arguments[n];
00816 if (arg.size() != sz ||
00817 !std::equal(i_bra, i, arg.begin()))
00818 continue;
00819 *buffer << strings_[token.stack_pos + n + 1]
00820 << "\376line " << macro_num
00821 << ' ' << val.location << "\n\376textdomain "
00822 << macro_textdomain << '\n';
00823 i_bra = i_end;
00824 break;
00825 }
00826 if (i_bra != i_end) {
00827
00828 buffer->write(&*i_bra - 1, sz + 2);
00829 i_bra = i_end;
00830 }
00831 }
00832 }
00833
00834 pop_token();
00835 std::string const &dir = directory_name(val.location.substr(0, val.location.find(' ')));
00836 if (!slowpath_) {
00837 DBG_CF << "substituting macro " << symbol << '\n';
00838 new preprocessor_data(target_, called_macros_, buffer, val.location, "",
00839 val.linenum, dir, val.textdomain, &symbol);
00840 } else {
00841 DBG_CF << "substituting (slow) macro " << symbol << '\n';
00842 std::ostringstream res;
00843 preprocessor_streambuf *buf =
00844 new preprocessor_streambuf(target_);
00845 { std::istream in(buf);
00846 new preprocessor_data(*buf, called_macros_, buffer, val.location, "",
00847 val.linenum, dir, val.textdomain, &symbol);
00848 res << in.rdbuf(); }
00849 delete buf;
00850 strings_.back() += res.str();
00851 }
00852 } else if (target_.depth_ < 40) {
00853 LOG_CF << "Macro definition not found for " << symbol << " , attempting to open as file.\n";
00854 pop_token();
00855 std::string prefix;
00856 std::string nfname;
00857 std::string const &newfilename = symbol;
00858
00859
00860
00861
00862
00863 if(newfilename != "" && (newfilename[0] == '~' || newfilename[0] == '@')) {
00864 nfname = newfilename;
00865 nfname.erase(nfname.begin(),nfname.begin()+1);
00866 nfname = get_user_data_dir() + "/data/" + nfname;
00867
00868 LOG_CF << "got relative name '" << newfilename
00869 << "' -> '" << nfname << "'\n";
00870
00871 if(newfilename[0] == '@' && file_exists(nfname) == false
00872 && is_directory(nfname) == false)
00873 {
00874 nfname = "data/" + newfilename.substr(1);
00875 }
00876 } else if(newfilename.size() >= 2 && newfilename[0] == '.'
00877 && newfilename[1] == '/' )
00878 {
00879
00880
00881
00882 nfname = newfilename;
00883 nfname.erase(nfname.begin(),nfname.begin()+2);
00884 nfname = directory_ + nfname;
00885 } else {
00886 nfname = "data/" + newfilename;
00887 }
00888
00889
00890 if (newfilename.rfind("../", 0) == std::string::npos
00891 && newfilename.find("/../") == std::string::npos)
00892 {
00893 if (!slowpath_)
00894 new preprocessor_file(target_, called_macros_, nfname);
00895 else {
00896 std::ostringstream res;
00897 preprocessor_streambuf *buf =
00898 new preprocessor_streambuf(target_);
00899 { std::istream in(buf);
00900 new preprocessor_file(*buf, called_macros_, nfname);
00901 res << in.rdbuf(); }
00902 delete buf;
00903 strings_.back() += res.str();
00904 }
00905 } else {
00906 ERR_CF << "Illegal path '" << newfilename
00907 << "' found (../ not allowed).\n";
00908 }
00909 } else {
00910 ERR_CF << "Too much nested preprocessing inclusions at "
00911 << linenum_ << ' ' << target_.location_
00912 << ". Aborting.\n";
00913 pop_token();
00914 }
00915 } else {
00916 strings_.back() += c;
00917 token.type = '[';
00918 }
00919 } else
00920 put(c);
00921 return true;
00922 }
00923
00924 struct preprocessor_deleter: std::basic_istream<char>
00925 {
00926 preprocessor_streambuf *buf_;
00927 preproc_map *defines_;
00928 std::vector<std::string> *callstack_;
00929 std::string *error_log;
00930 preprocessor_deleter(preprocessor_streambuf *buf, preproc_map *defines, std::vector<std::string>*);
00931 ~preprocessor_deleter();
00932 };
00933
00934 preprocessor_deleter::preprocessor_deleter(preprocessor_streambuf *buf,
00935 preproc_map *defines,
00936 std::vector<std::string> *callstack)
00937 : std::basic_istream<char>(buf), buf_(buf), defines_(defines), callstack_(callstack), error_log(buf->error_log)
00938 {
00939 }
00940
00941 preprocessor_deleter::~preprocessor_deleter()
00942 {
00943 rdbuf(NULL);
00944 delete buf_;
00945 delete defines_;
00946 delete callstack_;
00947 }
00948
00949
00950 std::istream *preprocess_file(std::string const &fname,
00951 preproc_map *defines, std::string *error_log)
00952 {
00953 log_scope("preprocessing file...");
00954 preproc_map *owned_defines = NULL;
00955 if (!defines) {
00956
00957
00958
00959
00960 owned_defines = new preproc_map;
00961 defines = owned_defines;
00962 }
00963 preprocessor_streambuf *buf = new preprocessor_streambuf(defines, error_log);
00964 std::vector<std::string> *callstack=new std::vector<std::string>;
00965 new preprocessor_file(*buf, callstack, fname);
00966 if(error_log!=NULL&&error_log->empty()==false)
00967 throw preproc_config::error("Error preprocessing files.");
00968 return new preprocessor_deleter(buf, owned_defines,callstack);
00969 }
00970