LCOV - code coverage report
Current view: top level - externals/simplecpp - simplecpp.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 2190 0.0 %
Date: 2024-04-28 12:00:40 Functions: 0 137 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * simplecpp - A simple and high-fidelity C/C++ preprocessor library
       3             :  * Copyright (C) 2016-2023 simplecpp team
       4             :  */
       5             : 
       6             : #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
       7             : #define SIMPLECPP_WINDOWS
       8             : #define NOMINMAX
       9             : #endif
      10             : 
      11             : #include "simplecpp.h"
      12             : 
      13             : #include <algorithm>
      14             : #include <cassert>
      15             : #include <cctype>
      16             : #include <climits>
      17             : #include <cstddef>
      18             : #include <cstdio>
      19             : #include <cstdlib>
      20             : #include <cstring>
      21             : #include <ctime>
      22             : #include <exception>
      23             : #include <fstream> // IWYU pragma: keep
      24             : #include <iostream>
      25             : #include <limits>
      26             : #include <list>
      27             : #include <map>
      28             : #include <set>
      29             : #include <sstream> // IWYU pragma: keep
      30             : #include <stack>
      31             : #include <stdexcept>
      32             : #include <string>
      33             : #if __cplusplus >= 201103L
      34             : #ifdef SIMPLECPP_WINDOWS
      35             : #include <mutex>
      36             : #endif
      37             : #include <unordered_map>
      38             : #endif
      39             : #include <utility>
      40             : #include <vector>
      41             : 
      42             : #ifdef SIMPLECPP_WINDOWS
      43             : #include <windows.h>
      44             : #undef ERROR
      45             : #endif
      46             : 
      47             : #if __cplusplus >= 201103L
      48             : #define OVERRIDE override
      49             : #define EXPLICIT explicit
      50             : #else
      51             : #define OVERRIDE
      52             : #define EXPLICIT
      53             : #endif
      54             : 
      55             : #if (__cplusplus < 201103L) && !defined(__APPLE__)
      56             : #define nullptr NULL
      57             : #endif
      58             : 
      59           0 : static bool isHex(const std::string &s)
      60             : {
      61           0 :     return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0);
      62             : }
      63             : 
      64           0 : static bool isOct(const std::string &s)
      65             : {
      66           0 :     return s.size()>1 && (s[0]=='0') && (s[1] >= '0') && (s[1] < '8');
      67             : }
      68             : 
      69             : // TODO: added an undercore since this conflicts with a function of the same name in utils.h from Cppcheck source when building Cppcheck with MSBuild
      70           0 : static bool isStringLiteral_(const std::string &s)
      71             : {
      72           0 :     return s.size() > 1 && (s[0]=='\"') && (*s.rbegin()=='\"');
      73             : }
      74             : 
      75             : // TODO: added an undercore since this conflicts with a function of the same name in utils.h from Cppcheck source when building Cppcheck with MSBuild
      76           0 : static bool isCharLiteral_(const std::string &s)
      77             : {
      78             :     // char literal patterns can include 'a', '\t', '\000', '\xff', 'abcd', and maybe ''
      79             :     // This only checks for the surrounding '' but doesn't parse the content.
      80           0 :     return s.size() > 1 && (s[0]=='\'') && (*s.rbegin()=='\'');
      81             : }
      82             : 
      83             : static const simplecpp::TokenString DEFINE("define");
      84             : static const simplecpp::TokenString UNDEF("undef");
      85             : 
      86             : static const simplecpp::TokenString INCLUDE("include");
      87             : 
      88             : static const simplecpp::TokenString ERROR("error");
      89             : static const simplecpp::TokenString WARNING("warning");
      90             : 
      91             : static const simplecpp::TokenString IF("if");
      92             : static const simplecpp::TokenString IFDEF("ifdef");
      93             : static const simplecpp::TokenString IFNDEF("ifndef");
      94             : static const simplecpp::TokenString DEFINED("defined");
      95             : static const simplecpp::TokenString ELSE("else");
      96             : static const simplecpp::TokenString ELIF("elif");
      97             : static const simplecpp::TokenString ENDIF("endif");
      98             : 
      99             : static const simplecpp::TokenString PRAGMA("pragma");
     100             : static const simplecpp::TokenString ONCE("once");
     101             : 
     102             : static const simplecpp::TokenString HAS_INCLUDE("__has_include");
     103             : 
     104             : static const simplecpp::TokenString INNER_COMMA(",,");
     105             : 
     106           0 : template<class T> static std::string toString(T t)
     107             : {
     108             :     // NOLINTNEXTLINE(misc-const-correctness) - false positive
     109           0 :     std::ostringstream ostr;
     110           0 :     ostr << t;
     111           0 :     return ostr.str();
     112             : }
     113             : 
     114           0 : static long long stringToLL(const std::string &s)
     115             : {
     116             :     long long ret;
     117           0 :     const bool hex = isHex(s);
     118           0 :     const bool oct = isOct(s);
     119           0 :     std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s);
     120           0 :     if (hex)
     121           0 :         istr >> std::hex;
     122           0 :     else if (oct)
     123           0 :         istr >> std::oct;
     124           0 :     istr >> ret;
     125           0 :     return ret;
     126             : }
     127             : 
     128           0 : static unsigned long long stringToULL(const std::string &s)
     129             : {
     130             :     unsigned long long ret;
     131           0 :     const bool hex = isHex(s);
     132           0 :     const bool oct = isOct(s);
     133           0 :     std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s);
     134           0 :     if (hex)
     135           0 :         istr >> std::hex;
     136           0 :     else if (oct)
     137           0 :         istr >> std::oct;
     138           0 :     istr >> ret;
     139           0 :     return ret;
     140             : }
     141             : 
     142           0 : static bool endsWith(const std::string &s, const std::string &e)
     143             : {
     144           0 :     return (s.size() >= e.size()) && std::equal(e.rbegin(), e.rend(), s.rbegin());
     145             : }
     146             : 
     147           0 : static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
     148             : {
     149           0 :     return tok1 && tok2 && tok1->location.sameline(tok2->location);
     150             : }
     151             : 
     152           0 : static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt)
     153             : {
     154           0 :     return (tok->name &&
     155           0 :             tok->str() == alt &&
     156           0 :             tok->previous &&
     157           0 :             tok->next &&
     158           0 :             (tok->previous->number || tok->previous->name || tok->previous->op == ')') &&
     159           0 :             (tok->next->number || tok->next->name || tok->next->op == '('));
     160             : }
     161             : 
     162           0 : static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt)
     163             : {
     164           0 :     return ((tok->name && tok->str() == alt) &&
     165           0 :             (!tok->previous || tok->previous->op == '(') &&
     166           0 :             (tok->next && (tok->next->name || tok->next->number)));
     167             : }
     168             : 
     169           0 : static std::string replaceAll(std::string s, const std::string& from, const std::string& to)
     170             : {
     171           0 :     for (size_t pos = s.find(from); pos != std::string::npos; pos = s.find(from, pos + to.size()))
     172           0 :         s.replace(pos, from.size(), to);
     173           0 :     return s;
     174             : }
     175             : 
     176             : const std::string simplecpp::Location::emptyFileName;
     177             : 
     178           0 : void simplecpp::Location::adjust(const std::string &str)
     179             : {
     180           0 :     if (strpbrk(str.c_str(), "\r\n") == nullptr) {
     181           0 :         col += str.size();
     182           0 :         return;
     183             :     }
     184             : 
     185           0 :     for (std::size_t i = 0U; i < str.size(); ++i) {
     186           0 :         col++;
     187           0 :         if (str[i] == '\n' || str[i] == '\r') {
     188           0 :             col = 1;
     189           0 :             line++;
     190           0 :             if (str[i] == '\r' && (i+1)<str.size() && str[i+1]=='\n')
     191           0 :                 ++i;
     192             :         }
     193             :     }
     194             : }
     195             : 
     196           0 : bool simplecpp::Token::isOneOf(const char ops[]) const
     197             : {
     198           0 :     return (op != '\0') && (std::strchr(ops, op) != nullptr);
     199             : }
     200             : 
     201           0 : bool simplecpp::Token::startsWithOneOf(const char c[]) const
     202             : {
     203           0 :     return std::strchr(c, string[0]) != nullptr;
     204             : }
     205             : 
     206           0 : bool simplecpp::Token::endsWithOneOf(const char c[]) const
     207             : {
     208           0 :     return std::strchr(c, string[string.size() - 1U]) != nullptr;
     209             : }
     210             : 
     211           0 : void simplecpp::Token::printAll() const
     212             : {
     213           0 :     const Token *tok = this;
     214           0 :     while (tok->previous)
     215           0 :         tok = tok->previous;
     216           0 :     for (; tok; tok = tok->next) {
     217           0 :         if (tok->previous) {
     218           0 :             std::cout << (sameline(tok, tok->previous) ? ' ' : '\n');
     219             :         }
     220           0 :         std::cout << tok->str();
     221             :     }
     222           0 :     std::cout << std::endl;
     223           0 : }
     224             : 
     225           0 : void simplecpp::Token::printOut() const
     226             : {
     227           0 :     for (const Token *tok = this; tok; tok = tok->next) {
     228           0 :         if (tok != this) {
     229           0 :             std::cout << (sameline(tok, tok->previous) ? ' ' : '\n');
     230             :         }
     231           0 :         std::cout << tok->str();
     232             :     }
     233           0 :     std::cout << std::endl;
     234           0 : }
     235             : 
     236             : // cppcheck-suppress noConstructor - we call init() in the inherited to initialize the private members
     237             : class simplecpp::TokenList::Stream {
     238             : public:
     239           0 :     virtual ~Stream() {}
     240             : 
     241             :     virtual int get() = 0;
     242             :     virtual int peek() = 0;
     243             :     virtual void unget() = 0;
     244             :     virtual bool good() = 0;
     245             : 
     246           0 :     unsigned char readChar()
     247             :     {
     248           0 :         unsigned char ch = static_cast<unsigned char>(get());
     249             : 
     250             :         // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the
     251             :         // character is non-ASCII character then replace it with 0xff
     252           0 :         if (isUtf16) {
     253           0 :             const unsigned char ch2 = static_cast<unsigned char>(get());
     254           0 :             const int ch16 = makeUtf16Char(ch, ch2);
     255           0 :             ch = static_cast<unsigned char>(((ch16 >= 0x80) ? 0xff : ch16));
     256             :         }
     257             : 
     258             :         // Handling of newlines..
     259           0 :         if (ch == '\r') {
     260           0 :             ch = '\n';
     261             : 
     262           0 :             int ch2 = get();
     263           0 :             if (isUtf16) {
     264           0 :                 const int c2 = get();
     265           0 :                 ch2 = makeUtf16Char(ch2, c2);
     266             :             }
     267             : 
     268           0 :             if (ch2 != '\n')
     269           0 :                 ungetChar();
     270             :         }
     271             : 
     272           0 :         return ch;
     273             :     }
     274             : 
     275           0 :     unsigned char peekChar()
     276             :     {
     277           0 :         unsigned char ch = static_cast<unsigned char>(peek());
     278             : 
     279             :         // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the
     280             :         // character is non-ASCII character then replace it with 0xff
     281           0 :         if (isUtf16) {
     282           0 :             (void)get();
     283           0 :             const unsigned char ch2 = static_cast<unsigned char>(peek());
     284           0 :             unget();
     285           0 :             const int ch16 = makeUtf16Char(ch, ch2);
     286           0 :             ch = static_cast<unsigned char>(((ch16 >= 0x80) ? 0xff : ch16));
     287             :         }
     288             : 
     289             :         // Handling of newlines..
     290           0 :         if (ch == '\r')
     291           0 :             ch = '\n';
     292             : 
     293           0 :         return ch;
     294             :     }
     295             : 
     296           0 :     void ungetChar()
     297             :     {
     298           0 :         unget();
     299           0 :         if (isUtf16)
     300           0 :             unget();
     301           0 :     }
     302             : 
     303             : protected:
     304           0 :     void init() {
     305             :         // initialize since we use peek() in getAndSkipBOM()
     306           0 :         isUtf16 = false;
     307           0 :         bom = getAndSkipBOM();
     308           0 :         isUtf16 = (bom == 0xfeff || bom == 0xfffe);
     309           0 :     }
     310             : 
     311             : private:
     312           0 :     inline int makeUtf16Char(const unsigned char ch, const unsigned char ch2) const
     313             :     {
     314           0 :         return (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch);
     315             :     }
     316             : 
     317           0 :     unsigned short getAndSkipBOM()
     318             :     {
     319           0 :         const int ch1 = peek();
     320             : 
     321             :         // The UTF-16 BOM is 0xfffe or 0xfeff.
     322           0 :         if (ch1 >= 0xfe) {
     323           0 :             (void)get();
     324           0 :             const unsigned short byte = (static_cast<unsigned char>(ch1) << 8);
     325           0 :             if (peek() >= 0xfe)
     326           0 :                 return byte | static_cast<unsigned char>(get());
     327           0 :             unget();
     328           0 :             return 0;
     329             :         }
     330             : 
     331             :         // Skip UTF-8 BOM 0xefbbbf
     332           0 :         if (ch1 == 0xef) {
     333           0 :             (void)get();
     334           0 :             if (peek() == 0xbb) {
     335           0 :                 (void)get();
     336           0 :                 if (peek() == 0xbf) {
     337           0 :                     (void)get();
     338           0 :                     return 0;
     339             :                 }
     340           0 :                 unget();
     341             :             }
     342           0 :             unget();
     343             :         }
     344             : 
     345           0 :         return 0;
     346             :     }
     347             : 
     348             :     unsigned short bom;
     349             : protected:
     350             :     bool isUtf16;
     351             : };
     352             : 
     353             : class StdIStream : public simplecpp::TokenList::Stream {
     354             : public:
     355             :     // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members
     356           0 :     EXPLICIT StdIStream(std::istream &istr)
     357           0 :         : istr(istr)
     358             :     {
     359           0 :         assert(istr.good());
     360           0 :         init();
     361           0 :     }
     362             : 
     363           0 :     virtual int get() OVERRIDE {
     364           0 :         return istr.get();
     365             :     }
     366           0 :     virtual int peek() OVERRIDE {
     367           0 :         return istr.peek();
     368             :     }
     369           0 :     virtual void unget() OVERRIDE {
     370           0 :         istr.unget();
     371           0 :     }
     372           0 :     virtual bool good() OVERRIDE {
     373           0 :         return istr.good();
     374             :     }
     375             : 
     376             : private:
     377             :     std::istream &istr;
     378             : };
     379             : 
     380             : class FileStream : public simplecpp::TokenList::Stream {
     381             : public:
     382             :     // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members
     383           0 :     EXPLICIT FileStream(const std::string &filename, std::vector<std::string> &files)
     384           0 :         : file(fopen(filename.c_str(), "rb"))
     385             :         , lastCh(0)
     386           0 :         , lastStatus(0)
     387             :     {
     388           0 :         if (!file) {
     389           0 :             files.push_back(filename);
     390           0 :             throw simplecpp::Output(files, simplecpp::Output::FILE_NOT_FOUND, "File is missing: " + filename);
     391             :         }
     392           0 :         init();
     393           0 :     }
     394             : 
     395           0 :     ~FileStream() OVERRIDE {
     396           0 :         fclose(file);
     397           0 :         file = nullptr;
     398           0 :     }
     399             : 
     400           0 :     virtual int get() OVERRIDE {
     401           0 :         lastStatus = lastCh = fgetc(file);
     402           0 :         return lastCh;
     403             :     }
     404           0 :     virtual int peek() OVERRIDE{
     405             :         // keep lastCh intact
     406           0 :         const int ch = fgetc(file);
     407           0 :         unget_internal(ch);
     408           0 :         return ch;
     409             :     }
     410           0 :     virtual void unget() OVERRIDE {
     411           0 :         unget_internal(lastCh);
     412           0 :     }
     413           0 :     virtual bool good() OVERRIDE {
     414           0 :         return lastStatus != EOF;
     415             :     }
     416             : 
     417             : private:
     418           0 :     void unget_internal(int ch) {
     419           0 :         if (isUtf16) {
     420             :             // TODO: use ungetc() as well
     421             :             // UTF-16 has subsequent unget() calls
     422           0 :             fseek(file, -1, SEEK_CUR);
     423             :         }
     424             :         else
     425           0 :             ungetc(ch, file);
     426           0 :     }
     427             : 
     428             :     FileStream(const FileStream&);
     429             :     FileStream &operator=(const FileStream&);
     430             : 
     431             :     FILE *file;
     432             :     int lastCh;
     433             :     int lastStatus;
     434             : };
     435             : 
     436           0 : simplecpp::TokenList::TokenList(std::vector<std::string> &filenames) : frontToken(nullptr), backToken(nullptr), files(filenames) {}
     437             : 
     438           0 : simplecpp::TokenList::TokenList(std::istream &istr, std::vector<std::string> &filenames, const std::string &filename, OutputList *outputList)
     439           0 :     : frontToken(nullptr), backToken(nullptr), files(filenames)
     440             : {
     441           0 :     StdIStream stream(istr);
     442           0 :     readfile(stream,filename,outputList);
     443           0 : }
     444             : 
     445           0 : simplecpp::TokenList::TokenList(const std::string &filename, std::vector<std::string> &filenames, OutputList *outputList)
     446           0 :         : frontToken(nullptr), backToken(nullptr), files(filenames)
     447             : {
     448             :     try
     449             :     {
     450           0 :         FileStream stream(filename, filenames);
     451           0 :         readfile(stream,filename,outputList);
     452             :     }
     453           0 :     catch(const simplecpp::Output & e) // TODO handle extra type of errors
     454             :     {
     455           0 :         outputList->push_back(e);
     456             :     }
     457           0 : }
     458             : 
     459           0 : simplecpp::TokenList::TokenList(const TokenList &other) : frontToken(nullptr), backToken(nullptr), files(other.files)
     460             : {
     461           0 :     *this = other;
     462           0 : }
     463             : 
     464             : #if __cplusplus >= 201103L
     465           0 : simplecpp::TokenList::TokenList(TokenList &&other) : frontToken(nullptr), backToken(nullptr), files(other.files)
     466             : {
     467           0 :     *this = std::move(other);
     468           0 : }
     469             : #endif
     470             : 
     471           0 : simplecpp::TokenList::~TokenList()
     472             : {
     473           0 :     clear();
     474           0 : }
     475             : 
     476           0 : simplecpp::TokenList &simplecpp::TokenList::operator=(const TokenList &other)
     477             : {
     478           0 :     if (this != &other) {
     479           0 :         clear();
     480           0 :         files = other.files;
     481           0 :         for (const Token *tok = other.cfront(); tok; tok = tok->next)
     482           0 :             push_back(new Token(*tok));
     483           0 :         sizeOfType = other.sizeOfType;
     484             :     }
     485           0 :     return *this;
     486             : }
     487             : 
     488             : #if __cplusplus >= 201103L
     489           0 : simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other)
     490             : {
     491           0 :     if (this != &other) {
     492           0 :         clear();
     493           0 :         frontToken = other.frontToken;
     494           0 :         other.frontToken = nullptr;
     495           0 :         backToken = other.backToken;
     496           0 :         other.backToken = nullptr;
     497           0 :         files = other.files;
     498           0 :         sizeOfType = std::move(other.sizeOfType);
     499             :     }
     500           0 :     return *this;
     501             : }
     502             : #endif
     503             : 
     504           0 : void simplecpp::TokenList::clear()
     505             : {
     506           0 :     backToken = nullptr;
     507           0 :     while (frontToken) {
     508           0 :         Token * const next = frontToken->next;
     509           0 :         delete frontToken;
     510           0 :         frontToken = next;
     511             :     }
     512           0 :     sizeOfType.clear();
     513           0 : }
     514             : 
     515           0 : void simplecpp::TokenList::push_back(Token *tok)
     516             : {
     517           0 :     if (!frontToken)
     518           0 :         frontToken = tok;
     519             :     else
     520           0 :         backToken->next = tok;
     521           0 :     tok->previous = backToken;
     522           0 :     backToken = tok;
     523           0 : }
     524             : 
     525           0 : void simplecpp::TokenList::dump() const
     526             : {
     527           0 :     std::cout << stringify() << std::endl;
     528           0 : }
     529             : 
     530           0 : std::string simplecpp::TokenList::stringify() const
     531             : {
     532           0 :     std::ostringstream ret;
     533           0 :     Location loc(files);
     534           0 :     for (const Token *tok = cfront(); tok; tok = tok->next) {
     535           0 :         if (tok->location.line < loc.line || tok->location.fileIndex != loc.fileIndex) {
     536           0 :             ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
     537           0 :             loc = tok->location;
     538             :         }
     539             : 
     540           0 :         while (tok->location.line > loc.line) {
     541           0 :             ret << '\n';
     542           0 :             loc.line++;
     543             :         }
     544             : 
     545           0 :         if (sameline(tok->previous, tok))
     546           0 :             ret << ' ';
     547             : 
     548           0 :         ret << tok->str();
     549             : 
     550           0 :         loc.adjust(tok->str());
     551             :     }
     552             : 
     553           0 :     return ret.str();
     554             : }
     555             : 
     556           0 : static bool isNameChar(unsigned char ch)
     557             : {
     558           0 :     return std::isalnum(ch) || ch == '_' || ch == '$';
     559             : }
     560             : 
     561           0 : static std::string escapeString(const std::string &str)
     562             : {
     563           0 :     std::ostringstream ostr;
     564           0 :     ostr << '\"';
     565           0 :     for (std::size_t i = 1U; i < str.size() - 1; ++i) {
     566           0 :         const char c = str[i];
     567           0 :         if (c == '\\' || c == '\"' || c == '\'')
     568           0 :             ostr << '\\';
     569           0 :         ostr << c;
     570             :     }
     571           0 :     ostr << '\"';
     572           0 :     return ostr.str();
     573             : }
     574             : 
     575           0 : static void portabilityBackslash(simplecpp::OutputList *outputList, const std::vector<std::string> &files, const simplecpp::Location &location)
     576             : {
     577           0 :     if (!outputList)
     578           0 :         return;
     579           0 :     simplecpp::Output err(files);
     580           0 :     err.type = simplecpp::Output::PORTABILITY_BACKSLASH;
     581           0 :     err.location = location;
     582           0 :     err.msg = "Combination 'backslash space newline' is not portable.";
     583           0 :     outputList->push_back(err);
     584             : }
     585             : 
     586           0 : static bool isStringLiteralPrefix(const std::string &str)
     587             : {
     588           0 :     return str == "u" || str == "U" || str == "L" || str == "u8" ||
     589           0 :            str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R";
     590             : }
     591             : 
     592           0 : void simplecpp::TokenList::lineDirective(unsigned int fileIndex, unsigned int line, Location *location)
     593             : {
     594           0 :     if (fileIndex != location->fileIndex || line >= location->line) {
     595           0 :         location->fileIndex = fileIndex;
     596           0 :         location->line = line;
     597           0 :         return;
     598             :     }
     599             : 
     600           0 :     if (line + 2 >= location->line) {
     601           0 :         location->line = line;
     602           0 :         while (cback()->op != '#')
     603           0 :             deleteToken(back());
     604           0 :         deleteToken(back());
     605           0 :         return;
     606             :     }
     607             : }
     608             : 
     609             : static const std::string COMMENT_END("*/");
     610             : 
     611           0 : void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, OutputList *outputList)
     612             : {
     613           0 :     std::stack<simplecpp::Location> loc;
     614             : 
     615           0 :     unsigned int multiline = 0U;
     616             : 
     617           0 :     const Token *oldLastToken = nullptr;
     618             : 
     619           0 :     Location location(files);
     620           0 :     location.fileIndex = fileIndex(filename);
     621           0 :     location.line = 1U;
     622           0 :     location.col  = 1U;
     623           0 :     while (stream.good()) {
     624           0 :         unsigned char ch = stream.readChar();
     625           0 :         if (!stream.good())
     626           0 :             break;
     627             : 
     628           0 :         if (ch >= 0x80) {
     629           0 :             if (outputList) {
     630           0 :                 simplecpp::Output err(files);
     631           0 :                 err.type = simplecpp::Output::UNHANDLED_CHAR_ERROR;
     632           0 :                 err.location = location;
     633           0 :                 std::ostringstream s;
     634           0 :                 s << static_cast<int>(ch);
     635           0 :                 err.msg = "The code contains unhandled character(s) (character code=" + s.str() + "). Neither unicode nor extended ascii is supported.";
     636           0 :                 outputList->push_back(err);
     637             :             }
     638           0 :             clear();
     639           0 :             return;
     640             :         }
     641             : 
     642           0 :         if (ch == '\n') {
     643           0 :             if (cback() && cback()->op == '\\') {
     644           0 :                 if (location.col > cback()->location.col + 1U)
     645           0 :                     portabilityBackslash(outputList, files, cback()->location);
     646           0 :                 ++multiline;
     647           0 :                 deleteToken(back());
     648             :             } else {
     649           0 :                 location.line += multiline + 1;
     650           0 :                 multiline = 0U;
     651             :             }
     652           0 :             if (!multiline)
     653           0 :                 location.col = 1;
     654             : 
     655           0 :             if (oldLastToken != cback()) {
     656           0 :                 oldLastToken = cback();
     657           0 :                 if (!isLastLinePreprocessor())
     658           0 :                     continue;
     659           0 :                 const std::string lastline(lastLine());
     660           0 :                 if (lastline == "# file %str%") {
     661           0 :                     const Token *strtok = cback();
     662           0 :                     while (strtok->comment)
     663           0 :                         strtok = strtok->previous;
     664           0 :                     loc.push(location);
     665           0 :                     location.fileIndex = fileIndex(strtok->str().substr(1U, strtok->str().size() - 2U));
     666           0 :                     location.line = 1U;
     667           0 :                 } else if (lastline == "# line %num%") {
     668           0 :                     const Token *numtok = cback();
     669           0 :                     while (numtok->comment)
     670           0 :                         numtok = numtok->previous;
     671           0 :                     lineDirective(location.fileIndex, std::atol(numtok->str().c_str()), &location);
     672           0 :                 } else if (lastline == "# %num% %str%" || lastline == "# line %num% %str%") {
     673           0 :                     const Token *strtok = cback();
     674           0 :                     while (strtok->comment)
     675           0 :                         strtok = strtok->previous;
     676           0 :                     const Token *numtok = strtok->previous;
     677           0 :                     while (numtok->comment)
     678           0 :                         numtok = numtok->previous;
     679           0 :                     lineDirective(fileIndex(replaceAll(strtok->str().substr(1U, strtok->str().size() - 2U),"\\\\","\\")),
     680           0 :                                   std::atol(numtok->str().c_str()), &location);
     681             :                 }
     682             :                 // #endfile
     683           0 :                 else if (lastline == "# endfile" && !loc.empty()) {
     684           0 :                     location = loc.top();
     685           0 :                     loc.pop();
     686             :                 }
     687             :             }
     688             : 
     689           0 :             continue;
     690             :         }
     691             : 
     692           0 :         if (ch <= ' ') {
     693           0 :             location.col++;
     694           0 :             continue;
     695             :         }
     696             : 
     697           0 :         TokenString currentToken;
     698             : 
     699           0 :         if (cback() && cback()->location.line == location.line && cback()->previous && cback()->previous->op == '#') {
     700           0 :             const Token* const llTok = lastLineTok();
     701           0 :             if (llTok && llTok->op == '#' && llTok->next && (llTok->next->str() == "error" || llTok->next->str() == "warning")) {
     702           0 :                 char prev = ' ';
     703           0 :                 while (stream.good() && (prev == '\\' || (ch != '\r' && ch != '\n'))) {
     704           0 :                     currentToken += ch;
     705           0 :                     prev = ch;
     706           0 :                     ch = stream.readChar();
     707             :                 }
     708           0 :                 stream.ungetChar();
     709           0 :                 push_back(new Token(currentToken, location));
     710           0 :                 location.adjust(currentToken);
     711           0 :                 continue;
     712             :             }
     713             :         }
     714             : 
     715             :         // number or name
     716           0 :         if (isNameChar(ch)) {
     717           0 :             const bool num = std::isdigit(ch);
     718           0 :             while (stream.good() && isNameChar(ch)) {
     719           0 :                 currentToken += ch;
     720           0 :                 ch = stream.readChar();
     721           0 :                 if (num && ch=='\'' && isNameChar(stream.peekChar()))
     722           0 :                     ch = stream.readChar();
     723             :             }
     724             : 
     725           0 :             stream.ungetChar();
     726             :         }
     727             : 
     728             :         // comment
     729           0 :         else if (ch == '/' && stream.peekChar() == '/') {
     730           0 :             while (stream.good() && ch != '\r' && ch != '\n') {
     731           0 :                 currentToken += ch;
     732           0 :                 ch = stream.readChar();
     733             :             }
     734           0 :             const std::string::size_type pos = currentToken.find_last_not_of(" \t");
     735           0 :             if (pos < currentToken.size() - 1U && currentToken[pos] == '\\')
     736           0 :                 portabilityBackslash(outputList, files, location);
     737           0 :             if (currentToken[currentToken.size() - 1U] == '\\') {
     738           0 :                 ++multiline;
     739           0 :                 currentToken.erase(currentToken.size() - 1U);
     740             :             } else {
     741           0 :                 stream.ungetChar();
     742             :             }
     743             :         }
     744             : 
     745             :         // comment
     746           0 :         else if (ch == '/' && stream.peekChar() == '*') {
     747           0 :             currentToken = "/*";
     748           0 :             (void)stream.readChar();
     749           0 :             ch = stream.readChar();
     750           0 :             while (stream.good()) {
     751           0 :                 currentToken += ch;
     752           0 :                 if (currentToken.size() >= 4U && endsWith(currentToken, COMMENT_END))
     753           0 :                     break;
     754           0 :                 ch = stream.readChar();
     755             :             }
     756             :             // multiline..
     757             : 
     758           0 :             std::string::size_type pos = 0;
     759           0 :             while ((pos = currentToken.find("\\\n",pos)) != std::string::npos) {
     760           0 :                 currentToken.erase(pos,2);
     761           0 :                 ++multiline;
     762             :             }
     763           0 :             if (multiline || isLastLinePreprocessor()) {
     764           0 :                 pos = 0;
     765           0 :                 while ((pos = currentToken.find('\n',pos)) != std::string::npos) {
     766           0 :                     currentToken.erase(pos,1);
     767           0 :                     ++multiline;
     768             :                 }
     769             :             }
     770             :         }
     771             : 
     772             :         // string / char literal
     773           0 :         else if (ch == '\"' || ch == '\'') {
     774           0 :             std::string prefix;
     775           0 :             if (cback() && cback()->name && isStringLiteralPrefix(cback()->str()) &&
     776           0 :                 ((cback()->location.col + cback()->str().size()) == location.col) &&
     777           0 :                 (cback()->location.line == location.line)) {
     778           0 :                 prefix = cback()->str();
     779             :             }
     780             :             // C++11 raw string literal
     781           0 :             if (ch == '\"' && !prefix.empty() && *cback()->str().rbegin() == 'R') {
     782           0 :                 std::string delim;
     783           0 :                 currentToken = ch;
     784           0 :                 prefix.resize(prefix.size() - 1);
     785           0 :                 ch = stream.readChar();
     786           0 :                 while (stream.good() && ch != '(' && ch != '\n') {
     787           0 :                     delim += ch;
     788           0 :                     ch = stream.readChar();
     789             :                 }
     790           0 :                 if (!stream.good() || ch == '\n') {
     791           0 :                     if (outputList) {
     792           0 :                         Output err(files);
     793           0 :                         err.type = Output::SYNTAX_ERROR;
     794           0 :                         err.location = location;
     795           0 :                         err.msg = "Invalid newline in raw string delimiter.";
     796           0 :                         outputList->push_back(err);
     797             :                     }
     798           0 :                     return;
     799             :                 }
     800           0 :                 const std::string endOfRawString(')' + delim + currentToken);
     801           0 :                 while (stream.good() && !(endsWith(currentToken, endOfRawString) && currentToken.size() > 1))
     802           0 :                     currentToken += stream.readChar();
     803           0 :                 if (!endsWith(currentToken, endOfRawString)) {
     804           0 :                     if (outputList) {
     805           0 :                         Output err(files);
     806           0 :                         err.type = Output::SYNTAX_ERROR;
     807           0 :                         err.location = location;
     808           0 :                         err.msg = "Raw string missing terminating delimiter.";
     809           0 :                         outputList->push_back(err);
     810             :                     }
     811           0 :                     return;
     812             :                 }
     813           0 :                 currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U);
     814           0 :                 currentToken = escapeString(currentToken);
     815           0 :                 currentToken.insert(0, prefix);
     816           0 :                 back()->setstr(currentToken);
     817           0 :                 location.adjust(currentToken);
     818           0 :                 if (currentToken.find_first_of("\r\n") == std::string::npos)
     819           0 :                     location.col += 2 + 2 * delim.size();
     820             :                 else
     821           0 :                     location.col += 1 + delim.size();
     822             : 
     823           0 :                 continue;
     824             :             }
     825             : 
     826           0 :             currentToken = readUntil(stream,location,ch,ch,outputList);
     827           0 :             if (currentToken.size() < 2U)
     828             :                 // Error is reported by readUntil()
     829           0 :                 return;
     830             : 
     831           0 :             std::string s = currentToken;
     832             :             std::string::size_type pos;
     833           0 :             int newlines = 0;
     834           0 :             while ((pos = s.find_first_of("\r\n")) != std::string::npos) {
     835           0 :                 s.erase(pos,1);
     836           0 :                 newlines++;
     837             :             }
     838             : 
     839           0 :             if (prefix.empty())
     840           0 :                 push_back(new Token(s, location)); // push string without newlines
     841             :             else
     842           0 :                 back()->setstr(prefix + s);
     843             : 
     844           0 :             if (newlines > 0 ) {
     845           0 :                 const Token * const llTok = lastLineTok();
     846           0 :                 if (llTok && llTok->op == '#' && llTok->next && llTok->next->str() == "define" && llTok->next->next) {
     847           0 :                     multiline += newlines;
     848           0 :                     location.adjust(s);
     849           0 :                     continue;
     850             :                 }
     851             :             }
     852             : 
     853           0 :             location.adjust(currentToken);
     854           0 :             continue;
     855             :         }
     856             : 
     857             :         else {
     858           0 :             currentToken += ch;
     859             :         }
     860             : 
     861           0 :         if (*currentToken.begin() == '<') {
     862           0 :             const Token * const llTok = lastLineTok();
     863           0 :             if (llTok && llTok->op == '#' && llTok->next && llTok->next->str() == "include") {
     864           0 :                 currentToken = readUntil(stream, location, '<', '>', outputList);
     865           0 :                 if (currentToken.size() < 2U)
     866           0 :                     return;
     867             :             }
     868             :         }
     869             : 
     870           0 :         push_back(new Token(currentToken, location));
     871             : 
     872           0 :         if (multiline)
     873           0 :             location.col += currentToken.size();
     874             :         else
     875           0 :             location.adjust(currentToken);
     876             :     }
     877             : 
     878           0 :     combineOperators();
     879             : }
     880             : 
     881           0 : void simplecpp::TokenList::constFold()
     882             : {
     883           0 :     while (cfront()) {
     884             :         // goto last '('
     885           0 :         Token *tok = back();
     886           0 :         while (tok && tok->op != '(')
     887           0 :             tok = tok->previous;
     888             : 
     889             :         // no '(', goto first token
     890           0 :         if (!tok)
     891           0 :             tok = front();
     892             : 
     893             :         // Constant fold expression
     894           0 :         constFoldUnaryNotPosNeg(tok);
     895           0 :         constFoldMulDivRem(tok);
     896           0 :         constFoldAddSub(tok);
     897           0 :         constFoldShift(tok);
     898           0 :         constFoldComparison(tok);
     899           0 :         constFoldBitwise(tok);
     900           0 :         constFoldLogicalOp(tok);
     901           0 :         constFoldQuestionOp(&tok);
     902             : 
     903             :         // If there is no '(' we are done with the constant folding
     904           0 :         if (tok->op != '(')
     905           0 :             break;
     906             : 
     907           0 :         if (!tok->next || !tok->next->next || tok->next->next->op != ')')
     908             :             break;
     909             : 
     910           0 :         tok = tok->next;
     911           0 :         deleteToken(tok->previous);
     912           0 :         deleteToken(tok->next);
     913             :     }
     914           0 : }
     915             : 
     916           0 : static bool isFloatSuffix(const simplecpp::Token *tok)
     917             : {
     918           0 :     if (!tok || tok->str().size() != 1U)
     919           0 :         return false;
     920           0 :     const char c = std::tolower(tok->str()[0]);
     921           0 :     return c == 'f' || c == 'l';
     922             : }
     923             : 
     924           0 : void simplecpp::TokenList::combineOperators()
     925             : {
     926           0 :     std::stack<bool> executableScope;
     927           0 :     executableScope.push(false);
     928           0 :     for (Token *tok = front(); tok; tok = tok->next) {
     929           0 :         if (tok->op == '{') {
     930           0 :             if (executableScope.top()) {
     931           0 :                 executableScope.push(true);
     932           0 :                 continue;
     933             :             }
     934           0 :             const Token *prev = tok->previous;
     935           0 :             while (prev && prev->isOneOf(";{}()"))
     936           0 :                 prev = prev->previous;
     937           0 :             executableScope.push(prev && prev->op == ')');
     938           0 :             continue;
     939             :         }
     940           0 :         if (tok->op == '}') {
     941           0 :             if (executableScope.size() > 1)
     942           0 :                 executableScope.pop();
     943           0 :             continue;
     944             :         }
     945             : 
     946           0 :         if (tok->op == '.') {
     947             :             // ellipsis ...
     948           0 :             if (tok->next && tok->next->op == '.' && tok->next->location.col == (tok->location.col + 1) &&
     949           0 :                 tok->next->next && tok->next->next->op == '.' && tok->next->next->location.col == (tok->location.col + 2)) {
     950           0 :                 tok->setstr("...");
     951           0 :                 deleteToken(tok->next);
     952           0 :                 deleteToken(tok->next);
     953           0 :                 continue;
     954             :             }
     955             :             // float literals..
     956           0 :             if (tok->previous && tok->previous->number && sameline(tok->previous, tok)) {
     957           0 :                 tok->setstr(tok->previous->str() + '.');
     958           0 :                 deleteToken(tok->previous);
     959           0 :                 if (sameline(tok, tok->next) && (isFloatSuffix(tok->next) || (tok->next && tok->next->startsWithOneOf("AaBbCcDdEeFfPp")))) {
     960           0 :                     tok->setstr(tok->str() + tok->next->str());
     961           0 :                     deleteToken(tok->next);
     962             :                 }
     963             :             }
     964           0 :             if (tok->next && tok->next->number) {
     965           0 :                 tok->setstr(tok->str() + tok->next->str());
     966           0 :                 deleteToken(tok->next);
     967             :             }
     968             :         }
     969             :         // match: [0-9.]+E [+-] [0-9]+
     970           0 :         const char lastChar = tok->str()[tok->str().size() - 1];
     971           0 :         if (tok->number && !isOct(tok->str()) &&
     972           0 :             ((!isHex(tok->str()) && (lastChar == 'E' || lastChar == 'e')) ||
     973           0 :              (isHex(tok->str()) && (lastChar == 'P' || lastChar == 'p'))) &&
     974           0 :             tok->next && tok->next->isOneOf("+-") && tok->next->next && tok->next->next->number) {
     975           0 :             tok->setstr(tok->str() + tok->next->op + tok->next->next->str());
     976           0 :             deleteToken(tok->next);
     977           0 :             deleteToken(tok->next);
     978             :         }
     979             : 
     980           0 :         if (tok->op == '\0' || !tok->next || tok->next->op == '\0')
     981           0 :             continue;
     982           0 :         if (!sameline(tok,tok->next))
     983           0 :             continue;
     984           0 :         if (tok->location.col + 1U != tok->next->location.col)
     985           0 :             continue;
     986             : 
     987           0 :         if (tok->next->op == '=' && tok->isOneOf("=!<>+-*/%&|^")) {
     988           0 :             if (tok->op == '&' && !executableScope.top()) {
     989             :                 // don't combine &= if it is a anonymous reference parameter with default value:
     990             :                 // void f(x&=2)
     991           0 :                 int indentlevel = 0;
     992           0 :                 const Token *start = tok;
     993           0 :                 while (indentlevel >= 0 && start) {
     994           0 :                     if (start->op == ')')
     995           0 :                         ++indentlevel;
     996           0 :                     else if (start->op == '(')
     997           0 :                         --indentlevel;
     998           0 :                     else if (start->isOneOf(";{}"))
     999           0 :                         break;
    1000           0 :                     start = start->previous;
    1001             :                 }
    1002           0 :                 if (indentlevel == -1 && start) {
    1003           0 :                     const Token * const ftok = start;
    1004           0 :                     bool isFuncDecl = ftok->name;
    1005           0 :                     while (isFuncDecl) {
    1006           0 :                         if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&')
    1007           0 :                             isFuncDecl = false;
    1008           0 :                         if (!start->previous)
    1009           0 :                             break;
    1010           0 :                         if (start->previous->isOneOf(";{}:"))
    1011           0 :                             break;
    1012           0 :                         start = start->previous;
    1013             :                     }
    1014           0 :                     isFuncDecl &= start != ftok && start->name;
    1015           0 :                     if (isFuncDecl) {
    1016             :                         // TODO: we could loop through the parameters here and check if they are correct.
    1017           0 :                         continue;
    1018             :                     }
    1019             :                 }
    1020             :             }
    1021           0 :             tok->setstr(tok->str() + "=");
    1022           0 :             deleteToken(tok->next);
    1023           0 :         } else if ((tok->op == '|' || tok->op == '&') && tok->op == tok->next->op) {
    1024           0 :             tok->setstr(tok->str() + tok->next->str());
    1025           0 :             deleteToken(tok->next);
    1026           0 :         } else if (tok->op == ':' && tok->next->op == ':') {
    1027           0 :             tok->setstr(tok->str() + tok->next->str());
    1028           0 :             deleteToken(tok->next);
    1029           0 :         } else if (tok->op == '-' && tok->next->op == '>') {
    1030           0 :             tok->setstr(tok->str() + tok->next->str());
    1031           0 :             deleteToken(tok->next);
    1032           0 :         } else if ((tok->op == '<' || tok->op == '>') && tok->op == tok->next->op) {
    1033           0 :             tok->setstr(tok->str() + tok->next->str());
    1034           0 :             deleteToken(tok->next);
    1035           0 :             if (tok->next && tok->next->op == '=' && tok->next->next && tok->next->next->op != '=') {
    1036           0 :                 tok->setstr(tok->str() + tok->next->str());
    1037           0 :                 deleteToken(tok->next);
    1038             :             }
    1039           0 :         } else if ((tok->op == '+' || tok->op == '-') && tok->op == tok->next->op) {
    1040           0 :             if (tok->location.col + 1U != tok->next->location.col)
    1041           0 :                 continue;
    1042           0 :             if (tok->previous && tok->previous->number)
    1043           0 :                 continue;
    1044           0 :             if (tok->next->next && tok->next->next->number)
    1045           0 :                 continue;
    1046           0 :             tok->setstr(tok->str() + tok->next->str());
    1047           0 :             deleteToken(tok->next);
    1048             :         }
    1049             :     }
    1050           0 : }
    1051             : 
    1052             : static const std::string COMPL("compl");
    1053             : static const std::string NOT("not");
    1054           0 : void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok)
    1055             : {
    1056           0 :     for (; tok && tok->op != ')'; tok = tok->next) {
    1057             :         // "not" might be !
    1058           0 :         if (isAlternativeUnaryOp(tok, NOT))
    1059           0 :             tok->op = '!';
    1060             :         // "compl" might be ~
    1061           0 :         else if (isAlternativeUnaryOp(tok, COMPL))
    1062           0 :             tok->op = '~';
    1063             : 
    1064           0 :         if (tok->op == '!' && tok->next && tok->next->number) {
    1065           0 :             tok->setstr(tok->next->str() == "0" ? "1" : "0");
    1066           0 :             deleteToken(tok->next);
    1067           0 :         } else if (tok->op == '~' && tok->next && tok->next->number) {
    1068           0 :             tok->setstr(toString(~stringToLL(tok->next->str())));
    1069           0 :             deleteToken(tok->next);
    1070             :         } else {
    1071           0 :             if (tok->previous && (tok->previous->number || tok->previous->name))
    1072           0 :                 continue;
    1073           0 :             if (!tok->next || !tok->next->number)
    1074           0 :                 continue;
    1075           0 :             switch (tok->op) {
    1076           0 :             case '+':
    1077           0 :                 tok->setstr(tok->next->str());
    1078           0 :                 deleteToken(tok->next);
    1079           0 :                 break;
    1080           0 :             case '-':
    1081           0 :                 tok->setstr(tok->op + tok->next->str());
    1082           0 :                 deleteToken(tok->next);
    1083           0 :                 break;
    1084             :             }
    1085             :         }
    1086             :     }
    1087           0 : }
    1088             : 
    1089           0 : void simplecpp::TokenList::constFoldMulDivRem(Token *tok)
    1090             : {
    1091           0 :     for (; tok && tok->op != ')'; tok = tok->next) {
    1092           0 :         if (!tok->previous || !tok->previous->number)
    1093           0 :             continue;
    1094           0 :         if (!tok->next || !tok->next->number)
    1095           0 :             continue;
    1096             : 
    1097             :         long long result;
    1098           0 :         if (tok->op == '*')
    1099           0 :             result = (stringToLL(tok->previous->str()) * stringToLL(tok->next->str()));
    1100           0 :         else if (tok->op == '/' || tok->op == '%') {
    1101           0 :             const long long rhs = stringToLL(tok->next->str());
    1102           0 :             if (rhs == 0)
    1103           0 :                 throw std::overflow_error("division/modulo by zero");
    1104           0 :             const long long lhs = stringToLL(tok->previous->str());
    1105           0 :             if (rhs == -1 && lhs == std::numeric_limits<long long>::min())
    1106           0 :                 throw std::overflow_error("division overflow");
    1107           0 :             if (tok->op == '/')
    1108           0 :                 result = (lhs / rhs);
    1109             :             else
    1110           0 :                 result = (lhs % rhs);
    1111             :         } else
    1112           0 :             continue;
    1113             : 
    1114           0 :         tok = tok->previous;
    1115           0 :         tok->setstr(toString(result));
    1116           0 :         deleteToken(tok->next);
    1117           0 :         deleteToken(tok->next);
    1118             :     }
    1119           0 : }
    1120             : 
    1121           0 : void simplecpp::TokenList::constFoldAddSub(Token *tok)
    1122             : {
    1123           0 :     for (; tok && tok->op != ')'; tok = tok->next) {
    1124           0 :         if (!tok->previous || !tok->previous->number)
    1125           0 :             continue;
    1126           0 :         if (!tok->next || !tok->next->number)
    1127           0 :             continue;
    1128             : 
    1129             :         long long result;
    1130           0 :         if (tok->op == '+')
    1131           0 :             result = stringToLL(tok->previous->str()) + stringToLL(tok->next->str());
    1132           0 :         else if (tok->op == '-')
    1133           0 :             result = stringToLL(tok->previous->str()) - stringToLL(tok->next->str());
    1134             :         else
    1135           0 :             continue;
    1136             : 
    1137           0 :         tok = tok->previous;
    1138           0 :         tok->setstr(toString(result));
    1139           0 :         deleteToken(tok->next);
    1140           0 :         deleteToken(tok->next);
    1141             :     }
    1142           0 : }
    1143             : 
    1144           0 : void simplecpp::TokenList::constFoldShift(Token *tok)
    1145             : {
    1146           0 :     for (; tok && tok->op != ')'; tok = tok->next) {
    1147           0 :         if (!tok->previous || !tok->previous->number)
    1148           0 :             continue;
    1149           0 :         if (!tok->next || !tok->next->number)
    1150           0 :             continue;
    1151             : 
    1152             :         long long result;
    1153           0 :         if (tok->str() == "<<")
    1154           0 :             result = stringToLL(tok->previous->str()) << stringToLL(tok->next->str());
    1155           0 :         else if (tok->str() == ">>")
    1156           0 :             result = stringToLL(tok->previous->str()) >> stringToLL(tok->next->str());
    1157             :         else
    1158           0 :             continue;
    1159             : 
    1160           0 :         tok = tok->previous;
    1161           0 :         tok->setstr(toString(result));
    1162           0 :         deleteToken(tok->next);
    1163           0 :         deleteToken(tok->next);
    1164             :     }
    1165           0 : }
    1166             : 
    1167             : static const std::string NOTEQ("not_eq");
    1168           0 : void simplecpp::TokenList::constFoldComparison(Token *tok)
    1169             : {
    1170           0 :     for (; tok && tok->op != ')'; tok = tok->next) {
    1171           0 :         if (isAlternativeBinaryOp(tok,NOTEQ))
    1172           0 :             tok->setstr("!=");
    1173             : 
    1174           0 :         if (!tok->startsWithOneOf("<>=!"))
    1175           0 :             continue;
    1176           0 :         if (!tok->previous || !tok->previous->number)
    1177           0 :             continue;
    1178           0 :         if (!tok->next || !tok->next->number)
    1179           0 :             continue;
    1180             : 
    1181             :         int result;
    1182           0 :         if (tok->str() == "==")
    1183           0 :             result = (stringToLL(tok->previous->str()) == stringToLL(tok->next->str()));
    1184           0 :         else if (tok->str() == "!=")
    1185           0 :             result = (stringToLL(tok->previous->str()) != stringToLL(tok->next->str()));
    1186           0 :         else if (tok->str() == ">")
    1187           0 :             result = (stringToLL(tok->previous->str()) > stringToLL(tok->next->str()));
    1188           0 :         else if (tok->str() == ">=")
    1189           0 :             result = (stringToLL(tok->previous->str()) >= stringToLL(tok->next->str()));
    1190           0 :         else if (tok->str() == "<")
    1191           0 :             result = (stringToLL(tok->previous->str()) < stringToLL(tok->next->str()));
    1192           0 :         else if (tok->str() == "<=")
    1193           0 :             result = (stringToLL(tok->previous->str()) <= stringToLL(tok->next->str()));
    1194             :         else
    1195           0 :             continue;
    1196             : 
    1197           0 :         tok = tok->previous;
    1198           0 :         tok->setstr(toString(result));
    1199           0 :         deleteToken(tok->next);
    1200           0 :         deleteToken(tok->next);
    1201             :     }
    1202           0 : }
    1203             : 
    1204             : static const std::string BITAND("bitand");
    1205             : static const std::string BITOR("bitor");
    1206             : static const std::string XOR("xor");
    1207           0 : void simplecpp::TokenList::constFoldBitwise(Token *tok)
    1208             : {
    1209           0 :     Token * const tok1 = tok;
    1210           0 :     for (const char *op = "&^|"; *op; op++) {
    1211             :         const std::string* alternativeOp;
    1212           0 :         if (*op == '&')
    1213           0 :             alternativeOp = &BITAND;
    1214           0 :         else if (*op == '|')
    1215           0 :             alternativeOp = &BITOR;
    1216             :         else
    1217           0 :             alternativeOp = &XOR;
    1218           0 :         for (tok = tok1; tok && tok->op != ')'; tok = tok->next) {
    1219           0 :             if (tok->op != *op && !isAlternativeBinaryOp(tok, *alternativeOp))
    1220           0 :                 continue;
    1221           0 :             if (!tok->previous || !tok->previous->number)
    1222           0 :                 continue;
    1223           0 :             if (!tok->next || !tok->next->number)
    1224           0 :                 continue;
    1225             :             long long result;
    1226           0 :             if (*op == '&')
    1227           0 :                 result = (stringToLL(tok->previous->str()) & stringToLL(tok->next->str()));
    1228           0 :             else if (*op == '^')
    1229           0 :                 result = (stringToLL(tok->previous->str()) ^ stringToLL(tok->next->str()));
    1230             :             else /*if (*op == '|')*/
    1231           0 :                 result = (stringToLL(tok->previous->str()) | stringToLL(tok->next->str()));
    1232           0 :             tok = tok->previous;
    1233           0 :             tok->setstr(toString(result));
    1234           0 :             deleteToken(tok->next);
    1235           0 :             deleteToken(tok->next);
    1236             :         }
    1237             :     }
    1238           0 : }
    1239             : 
    1240             : static const std::string AND("and");
    1241             : static const std::string OR("or");
    1242           0 : void simplecpp::TokenList::constFoldLogicalOp(Token *tok)
    1243             : {
    1244           0 :     for (; tok && tok->op != ')'; tok = tok->next) {
    1245           0 :         if (tok->name) {
    1246           0 :             if (isAlternativeBinaryOp(tok,AND))
    1247           0 :                 tok->setstr("&&");
    1248           0 :             else if (isAlternativeBinaryOp(tok,OR))
    1249           0 :                 tok->setstr("||");
    1250             :         }
    1251           0 :         if (tok->str() != "&&" && tok->str() != "||")
    1252           0 :             continue;
    1253           0 :         if (!tok->previous || !tok->previous->number)
    1254           0 :             continue;
    1255           0 :         if (!tok->next || !tok->next->number)
    1256           0 :             continue;
    1257             : 
    1258             :         int result;
    1259           0 :         if (tok->str() == "||")
    1260           0 :             result = (stringToLL(tok->previous->str()) || stringToLL(tok->next->str()));
    1261             :         else /*if (tok->str() == "&&")*/
    1262           0 :             result = (stringToLL(tok->previous->str()) && stringToLL(tok->next->str()));
    1263             : 
    1264           0 :         tok = tok->previous;
    1265           0 :         tok->setstr(toString(result));
    1266           0 :         deleteToken(tok->next);
    1267           0 :         deleteToken(tok->next);
    1268             :     }
    1269           0 : }
    1270             : 
    1271           0 : void simplecpp::TokenList::constFoldQuestionOp(Token **tok1)
    1272             : {
    1273           0 :     bool gotoTok1 = false;
    1274           0 :     for (Token *tok = *tok1; tok && tok->op != ')'; tok =  gotoTok1 ? *tok1 : tok->next) {
    1275           0 :         gotoTok1 = false;
    1276           0 :         if (tok->str() != "?")
    1277           0 :             continue;
    1278           0 :         if (!tok->previous || !tok->next || !tok->next->next)
    1279           0 :             throw std::runtime_error("invalid expression");
    1280           0 :         if (!tok->previous->number)
    1281           0 :             continue;
    1282           0 :         if (tok->next->next->op != ':')
    1283           0 :             continue;
    1284           0 :         Token * const condTok = tok->previous;
    1285           0 :         Token * const trueTok = tok->next;
    1286           0 :         Token * const falseTok = trueTok->next->next;
    1287           0 :         if (!falseTok)
    1288           0 :             throw std::runtime_error("invalid expression");
    1289           0 :         if (condTok == *tok1)
    1290           0 :             *tok1 = (condTok->str() != "0" ? trueTok : falseTok);
    1291           0 :         deleteToken(condTok->next); // ?
    1292           0 :         deleteToken(trueTok->next); // :
    1293           0 :         deleteToken(condTok->str() == "0" ? trueTok : falseTok);
    1294           0 :         deleteToken(condTok);
    1295           0 :         gotoTok1 = true;
    1296             :     }
    1297           0 : }
    1298             : 
    1299           0 : void simplecpp::TokenList::removeComments()
    1300             : {
    1301           0 :     Token *tok = frontToken;
    1302           0 :     while (tok) {
    1303           0 :         Token * const tok1 = tok;
    1304           0 :         tok = tok->next;
    1305           0 :         if (tok1->comment)
    1306           0 :             deleteToken(tok1);
    1307             :     }
    1308           0 : }
    1309             : 
    1310           0 : std::string simplecpp::TokenList::readUntil(Stream &stream, const Location &location, const char start, const char end, OutputList *outputList)
    1311             : {
    1312           0 :     std::string ret;
    1313           0 :     ret += start;
    1314             : 
    1315           0 :     bool backslash = false;
    1316           0 :     char ch = 0;
    1317           0 :     while (ch != end && ch != '\r' && ch != '\n' && stream.good()) {
    1318           0 :         ch = stream.readChar();
    1319           0 :         if (backslash && ch == '\n') {
    1320           0 :             ch = 0;
    1321           0 :             backslash = false;
    1322           0 :             continue;
    1323             :         }
    1324           0 :         backslash = false;
    1325           0 :         ret += ch;
    1326           0 :         if (ch == '\\') {
    1327           0 :             bool update_ch = false;
    1328           0 :             char next = 0;
    1329           0 :             do {
    1330           0 :                 next = stream.readChar();
    1331           0 :                 if (next == '\r' || next == '\n') {
    1332           0 :                     ret.erase(ret.size()-1U);
    1333           0 :                     backslash = (next == '\r');
    1334           0 :                     update_ch = false;
    1335           0 :                 } else if (next == '\\')
    1336           0 :                     update_ch = !update_ch;
    1337           0 :                 ret += next;
    1338           0 :             } while (next == '\\');
    1339           0 :             if (update_ch)
    1340           0 :                 ch = next;
    1341             :         }
    1342             :     }
    1343             : 
    1344           0 :     if (!stream.good() || ch != end) {
    1345           0 :         clear();
    1346           0 :         if (outputList) {
    1347           0 :             Output err(files);
    1348           0 :             err.type = Output::SYNTAX_ERROR;
    1349           0 :             err.location = location;
    1350           0 :             err.msg = std::string("No pair for character (") + start + "). Can't process file. File is either invalid or unicode, which is currently not supported.";
    1351           0 :             outputList->push_back(err);
    1352             :         }
    1353           0 :         return "";
    1354             :     }
    1355             : 
    1356           0 :     return ret;
    1357             : }
    1358             : 
    1359           0 : std::string simplecpp::TokenList::lastLine(int maxsize) const
    1360             : {
    1361           0 :     std::string ret;
    1362           0 :     int count = 0;
    1363           0 :     for (const Token *tok = cback(); ; tok = tok->previous) {
    1364           0 :         if (!sameline(tok, cback())) {
    1365           0 :             break;
    1366             :         }
    1367           0 :         if (tok->comment)
    1368           0 :             continue;
    1369           0 :         if (++count > maxsize)
    1370           0 :             return "";
    1371           0 :         if (!ret.empty())
    1372           0 :             ret += ' ';
    1373             :         // add tokens in reverse for performance reasons
    1374           0 :         if (tok->str()[0] == '\"')
    1375           0 :             ret += "%rts%"; // %str%
    1376           0 :         else if (tok->number)
    1377           0 :             ret += "%mun%"; // %num%
    1378             :         else {
    1379           0 :             ret += tok->str();
    1380           0 :             std::reverse(ret.end() - tok->str().length(), ret.end());
    1381             :         }
    1382             :     }
    1383           0 :     std::reverse(ret.begin(), ret.end());
    1384           0 :     return ret;
    1385             : }
    1386             : 
    1387           0 : const simplecpp::Token* simplecpp::TokenList::lastLineTok(int maxsize) const
    1388             : {
    1389           0 :     const Token* prevTok = nullptr;
    1390           0 :     int count = 0;
    1391           0 :     for (const Token *tok = cback(); ; tok = tok->previous) {
    1392           0 :         if (!sameline(tok, cback()))
    1393           0 :             break;
    1394           0 :         if (tok->comment)
    1395           0 :             continue;
    1396           0 :         if (++count > maxsize)
    1397           0 :             return nullptr;
    1398           0 :         prevTok = tok;
    1399             :     }
    1400           0 :     return prevTok;
    1401             : }
    1402             : 
    1403           0 : bool simplecpp::TokenList::isLastLinePreprocessor(int maxsize) const
    1404             : {
    1405           0 :     const Token * const prevTok = lastLineTok(maxsize);
    1406           0 :     return prevTok && prevTok->op == '#';
    1407             : }
    1408             : 
    1409           0 : unsigned int simplecpp::TokenList::fileIndex(const std::string &filename)
    1410             : {
    1411           0 :     for (unsigned int i = 0; i < files.size(); ++i) {
    1412           0 :         if (files[i] == filename)
    1413           0 :             return i;
    1414             :     }
    1415           0 :     files.push_back(filename);
    1416           0 :     return files.size() - 1U;
    1417             : }
    1418             : 
    1419             : 
    1420             : namespace simplecpp {
    1421             :     class Macro;
    1422             : #if __cplusplus >= 201103L
    1423             :     using MacroMap = std::unordered_map<TokenString,Macro>;
    1424             : #else
    1425             :     typedef std::map<TokenString,Macro> MacroMap;
    1426             : #endif
    1427             : 
    1428             :     class Macro {
    1429             :     public:
    1430             :         explicit Macro(std::vector<std::string> &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), valueDefinedInCode_(false) {}
    1431             : 
    1432           0 :         Macro(const Token *tok, std::vector<std::string> &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(true) {
    1433           0 :             if (sameline(tok->previousSkipComments(), tok))
    1434           0 :                 throw std::runtime_error("bad macro syntax");
    1435           0 :             if (tok->op != '#')
    1436           0 :                 throw std::runtime_error("bad macro syntax");
    1437           0 :             const Token * const hashtok = tok;
    1438           0 :             tok = tok->next;
    1439           0 :             if (!tok || tok->str() != DEFINE)
    1440           0 :                 throw std::runtime_error("bad macro syntax");
    1441           0 :             tok = tok->next;
    1442           0 :             if (!tok || !tok->name || !sameline(hashtok,tok))
    1443           0 :                 throw std::runtime_error("bad macro syntax");
    1444           0 :             if (!parseDefine(tok))
    1445           0 :                 throw std::runtime_error("bad macro syntax");
    1446           0 :         }
    1447             : 
    1448           0 :         Macro(const std::string &name, const std::string &value, std::vector<std::string> &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(false) {
    1449           0 :             const std::string def(name + ' ' + value);
    1450           0 :             std::istringstream istr(def);
    1451           0 :             StdIStream stream(istr);
    1452           0 :             tokenListDefine.readfile(stream);
    1453           0 :             if (!parseDefine(tokenListDefine.cfront()))
    1454           0 :                 throw std::runtime_error("bad macro syntax. macroname=" + name + " value=" + value);
    1455           0 :         }
    1456             : 
    1457           0 :         Macro(const Macro &other) : nameTokDef(nullptr), files(other.files), tokenListDefine(other.files), valueDefinedInCode_(other.valueDefinedInCode_) {
    1458           0 :             *this = other;
    1459           0 :         }
    1460             : 
    1461           0 :         Macro &operator=(const Macro &other) {
    1462           0 :             if (this != &other) {
    1463           0 :                 files = other.files;
    1464           0 :                 valueDefinedInCode_ = other.valueDefinedInCode_;
    1465           0 :                 if (other.tokenListDefine.empty())
    1466           0 :                     parseDefine(other.nameTokDef);
    1467             :                 else {
    1468           0 :                     tokenListDefine = other.tokenListDefine;
    1469           0 :                     parseDefine(tokenListDefine.cfront());
    1470             :                 }
    1471           0 :                 usageList = other.usageList;
    1472             :             }
    1473           0 :             return *this;
    1474             :         }
    1475             : 
    1476           0 :         bool valueDefinedInCode() const {
    1477           0 :             return valueDefinedInCode_;
    1478             :         }
    1479             : 
    1480             :         /**
    1481             :          * Expand macro. This will recursively expand inner macros.
    1482             :          * @param output     destination tokenlist
    1483             :          * @param rawtok     macro token
    1484             :          * @param macros     list of macros
    1485             :          * @param inputFiles the input files
    1486             :          * @return token after macro
    1487             :          * @throw Can throw wrongNumberOfParameters or invalidHashHash
    1488             :          */
    1489           0 :         const Token * expand(TokenList * const output,
    1490             :                              const Token * rawtok,
    1491             :                              const MacroMap &macros,
    1492             :                              std::vector<std::string> &inputFiles) const {
    1493           0 :             std::set<TokenString> expandedmacros;
    1494             : 
    1495           0 :             TokenList output2(inputFiles);
    1496             : 
    1497           0 :             if (functionLike() && rawtok->next && rawtok->next->op == '(') {
    1498             :                 // Copy macro call to a new tokenlist with no linebreaks
    1499           0 :                 const Token * const rawtok1 = rawtok;
    1500           0 :                 TokenList rawtokens2(inputFiles);
    1501           0 :                 rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location));
    1502           0 :                 rawtok = rawtok->next;
    1503           0 :                 rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location));
    1504           0 :                 rawtok = rawtok->next;
    1505           0 :                 int par = 1;
    1506           0 :                 while (rawtok && par > 0) {
    1507           0 :                     if (rawtok->op == '(')
    1508           0 :                         ++par;
    1509           0 :                     else if (rawtok->op == ')')
    1510           0 :                         --par;
    1511           0 :                     else if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok))
    1512           0 :                         throw Error(rawtok->location, "it is invalid to use a preprocessor directive as macro parameter");
    1513           0 :                     rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location));
    1514           0 :                     rawtok = rawtok->next;
    1515             :                 }
    1516           0 :                 bool first = true;
    1517           0 :                 if (valueToken && valueToken->str() == rawtok1->str())
    1518           0 :                     first = false;
    1519           0 :                 if (expand(&output2, rawtok1->location, rawtokens2.cfront(), macros, expandedmacros, first))
    1520           0 :                     rawtok = rawtok1->next;
    1521             :             } else {
    1522           0 :                 rawtok = expand(&output2, rawtok->location, rawtok, macros, expandedmacros);
    1523             :             }
    1524           0 :             while (output2.cback() && rawtok) {
    1525           0 :                 unsigned int par = 0;
    1526           0 :                 Token* macro2tok = output2.back();
    1527           0 :                 while (macro2tok) {
    1528           0 :                     if (macro2tok->op == '(') {
    1529           0 :                         if (par==0)
    1530           0 :                             break;
    1531           0 :                         --par;
    1532           0 :                     } else if (macro2tok->op == ')')
    1533           0 :                         ++par;
    1534           0 :                     macro2tok = macro2tok->previous;
    1535             :                 }
    1536           0 :                 if (macro2tok) { // macro2tok->op == '('
    1537           0 :                     macro2tok = macro2tok->previous;
    1538           0 :                     expandedmacros.insert(name());
    1539           0 :                 } else if (rawtok->op == '(')
    1540           0 :                     macro2tok = output2.back();
    1541           0 :                 if (!macro2tok || !macro2tok->name)
    1542             :                     break;
    1543           0 :                 if (output2.cfront() != output2.cback() && macro2tok->str() == this->name())
    1544           0 :                     break;
    1545           0 :                 const MacroMap::const_iterator macro = macros.find(macro2tok->str());
    1546           0 :                 if (macro == macros.end() || !macro->second.functionLike())
    1547           0 :                     break;
    1548           0 :                 TokenList rawtokens2(inputFiles);
    1549           0 :                 const Location loc(macro2tok->location);
    1550           0 :                 while (macro2tok) {
    1551           0 :                     Token * const next = macro2tok->next;
    1552           0 :                     rawtokens2.push_back(new Token(macro2tok->str(), loc));
    1553           0 :                     output2.deleteToken(macro2tok);
    1554           0 :                     macro2tok = next;
    1555             :                 }
    1556           0 :                 par = (rawtokens2.cfront() != rawtokens2.cback()) ? 1U : 0U;
    1557           0 :                 const Token *rawtok2 = rawtok;
    1558           0 :                 for (; rawtok2; rawtok2 = rawtok2->next) {
    1559           0 :                     rawtokens2.push_back(new Token(rawtok2->str(), loc));
    1560           0 :                     if (rawtok2->op == '(')
    1561           0 :                         ++par;
    1562           0 :                     else if (rawtok2->op == ')') {
    1563           0 :                         if (par <= 1U)
    1564           0 :                             break;
    1565           0 :                         --par;
    1566             :                     }
    1567             :                 }
    1568           0 :                 if (!rawtok2 || par != 1U)
    1569             :                     break;
    1570           0 :                 if (macro->second.expand(&output2, rawtok->location, rawtokens2.cfront(), macros, expandedmacros) != nullptr)
    1571           0 :                     break;
    1572           0 :                 rawtok = rawtok2->next;
    1573             :             }
    1574           0 :             output->takeTokens(output2);
    1575           0 :             for (Token* tok = output->front(); tok; tok = tok->next) {
    1576           0 :                 if (tok->str() == INNER_COMMA)
    1577           0 :                     tok->setstr(",");
    1578             :             }
    1579           0 :             return rawtok;
    1580             :         }
    1581             : 
    1582             :         /** macro name */
    1583           0 :         const TokenString &name() const {
    1584           0 :             return nameTokDef->str();
    1585             :         }
    1586             : 
    1587             :         /** location for macro definition */
    1588           0 :         const Location &defineLocation() const {
    1589           0 :             return nameTokDef->location;
    1590             :         }
    1591             : 
    1592             :         /** how has this macro been used so far */
    1593           0 :         const std::list<Location> &usage() const {
    1594           0 :             return usageList;
    1595             :         }
    1596             : 
    1597             :         /** is this a function like macro */
    1598           0 :         bool functionLike() const {
    1599           0 :             return nameTokDef->next &&
    1600           0 :                    nameTokDef->next->op == '(' &&
    1601           0 :                    sameline(nameTokDef, nameTokDef->next) &&
    1602           0 :                    nameTokDef->next->location.col == nameTokDef->location.col + nameTokDef->str().size();
    1603             :         }
    1604             : 
    1605             :         /** base class for errors */
    1606             :         struct Error {
    1607           0 :             Error(const Location &loc, const std::string &s) : location(loc), what(s) {}
    1608             :             const Location location;
    1609             :             const std::string what;
    1610             :         };
    1611             : 
    1612             :         /** Struct that is thrown when macro is expanded with wrong number of parameters */
    1613             :         struct wrongNumberOfParameters : public Error {
    1614           0 :             wrongNumberOfParameters(const Location &loc, const std::string &macroName) : Error(loc, "Wrong number of parameters for macro \'" + macroName + "\'.") {}
    1615             :         };
    1616             : 
    1617             :         /** Struct that is thrown when there is invalid ## usage */
    1618             :         struct invalidHashHash : public Error {
    1619           0 :             static inline std::string format(const std::string &macroName, const std::string &message) {
    1620           0 :                 return "Invalid ## usage when expanding \'" + macroName + "\': " + message;
    1621             :             }
    1622             : 
    1623           0 :             invalidHashHash(const Location &loc, const std::string &macroName, const std::string &message)
    1624           0 :                 : Error(loc, format(macroName, message)) { }
    1625             : 
    1626           0 :             static inline invalidHashHash unexpectedToken(const Location &loc, const std::string &macroName, const Token *tokenA) {
    1627           0 :                 return invalidHashHash(loc, macroName, "Unexpected token '"+ tokenA->str()+"'");
    1628             :             }
    1629             : 
    1630           0 :             static inline invalidHashHash cannotCombine(const Location &loc, const std::string &macroName, const Token *tokenA, const Token *tokenB) {
    1631           0 :                 return invalidHashHash(loc, macroName, "Combining '"+ tokenA->str()+ "' and '"+ tokenB->str() + "' yields an invalid token.");
    1632             :             }
    1633             : 
    1634           0 :             static inline invalidHashHash unexpectedNewline(const Location &loc, const std::string &macroName) {
    1635           0 :                 return invalidHashHash(loc, macroName, "Unexpected newline");
    1636             :             }
    1637             : 
    1638           0 :             static inline invalidHashHash universalCharacterUB(const Location &loc, const std::string &macroName, const Token* tokenA, const std::string& strAB) {
    1639           0 :                 return invalidHashHash(loc, macroName, "Combining '\\"+ tokenA->str()+ "' and '"+ strAB.substr(tokenA->str().size()) + "' yields universal character '\\" + strAB + "'. This is undefined behavior according to C standard chapter 5.1.1.2, paragraph 4.");
    1640             :             }
    1641             :         };
    1642             :     private:
    1643             :         /** Create new token where Token::macro is set for replaced tokens */
    1644           0 :         Token *newMacroToken(const TokenString &str, const Location &loc, bool replaced, const Token *expandedFromToken=nullptr) const {
    1645           0 :             Token *tok = new Token(str,loc);
    1646           0 :             if (replaced)
    1647           0 :                 tok->macro = nameTokDef->str();
    1648           0 :             if (expandedFromToken)
    1649           0 :                 tok->setExpandedFrom(expandedFromToken, this);
    1650           0 :             return tok;
    1651             :         }
    1652             : 
    1653           0 :         bool parseDefine(const Token *nametoken) {
    1654           0 :             nameTokDef = nametoken;
    1655           0 :             variadic = false;
    1656           0 :             if (!nameTokDef) {
    1657           0 :                 valueToken = endToken = nullptr;
    1658           0 :                 args.clear();
    1659           0 :                 return false;
    1660             :             }
    1661             : 
    1662             :             // function like macro..
    1663           0 :             if (functionLike()) {
    1664           0 :                 args.clear();
    1665           0 :                 const Token *argtok = nameTokDef->next->next;
    1666           0 :                 while (sameline(nametoken, argtok) && argtok->op != ')') {
    1667           0 :                     if (argtok->str() == "..." &&
    1668           0 :                         argtok->next && argtok->next->op == ')') {
    1669           0 :                         variadic = true;
    1670           0 :                         if (!argtok->previous->name)
    1671           0 :                             args.push_back("__VA_ARGS__");
    1672           0 :                         argtok = argtok->next; // goto ')'
    1673           0 :                         break;
    1674             :                     }
    1675           0 :                     if (argtok->op != ',')
    1676           0 :                         args.push_back(argtok->str());
    1677           0 :                     argtok = argtok->next;
    1678             :                 }
    1679           0 :                 if (!sameline(nametoken, argtok)) {
    1680           0 :                     endToken = argtok ? argtok->previous : argtok;
    1681           0 :                     valueToken = nullptr;
    1682           0 :                     return false;
    1683             :                 }
    1684           0 :                 valueToken = argtok ? argtok->next : nullptr;
    1685             :             } else {
    1686           0 :                 args.clear();
    1687           0 :                 valueToken = nameTokDef->next;
    1688             :             }
    1689             : 
    1690           0 :             if (!sameline(valueToken, nameTokDef))
    1691           0 :                 valueToken = nullptr;
    1692           0 :             endToken = valueToken;
    1693           0 :             while (sameline(endToken, nameTokDef))
    1694           0 :                 endToken = endToken->next;
    1695           0 :             return true;
    1696             :         }
    1697             : 
    1698           0 :         unsigned int getArgNum(const TokenString &str) const {
    1699           0 :             unsigned int par = 0;
    1700           0 :             while (par < args.size()) {
    1701           0 :                 if (str == args[par])
    1702           0 :                     return par;
    1703           0 :                 par++;
    1704             :             }
    1705           0 :             return ~0U;
    1706             :         }
    1707             : 
    1708           0 :         std::vector<const Token *> getMacroParameters(const Token *nameTokInst, bool calledInDefine) const {
    1709           0 :             if (!nameTokInst->next || nameTokInst->next->op != '(' || !functionLike())
    1710           0 :                 return std::vector<const Token *>();
    1711             : 
    1712           0 :             std::vector<const Token *> parametertokens;
    1713           0 :             parametertokens.push_back(nameTokInst->next);
    1714           0 :             unsigned int par = 0U;
    1715           0 :             for (const Token *tok = nameTokInst->next->next; calledInDefine ? sameline(tok, nameTokInst) : (tok != nullptr); tok = tok->next) {
    1716           0 :                 if (tok->op == '(')
    1717           0 :                     ++par;
    1718           0 :                 else if (tok->op == ')') {
    1719           0 :                     if (par == 0U) {
    1720           0 :                         parametertokens.push_back(tok);
    1721           0 :                         break;
    1722             :                     }
    1723           0 :                     --par;
    1724           0 :                 } else if (par == 0U && tok->op == ',' && (!variadic || parametertokens.size() < args.size()))
    1725           0 :                     parametertokens.push_back(tok);
    1726             :             }
    1727           0 :             return parametertokens;
    1728             :         }
    1729             : 
    1730           0 :         const Token *appendTokens(TokenList *tokens,
    1731             :                                   const Location &rawloc,
    1732             :                                   const Token * const lpar,
    1733             :                                   const MacroMap &macros,
    1734             :                                   const std::set<TokenString> &expandedmacros,
    1735             :                                   const std::vector<const Token*> &parametertokens) const {
    1736           0 :             if (!lpar || lpar->op != '(')
    1737           0 :                 return nullptr;
    1738           0 :             unsigned int par = 0;
    1739           0 :             const Token *tok = lpar;
    1740           0 :             while (sameline(lpar, tok)) {
    1741           0 :                 if (tok->op == '#' && sameline(tok,tok->next) && tok->next->op == '#' && sameline(tok,tok->next->next)) {
    1742             :                     // A##B => AB
    1743           0 :                     tok = expandHashHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens);
    1744           0 :                 } else if (tok->op == '#' && sameline(tok, tok->next) && tok->next->op != '#') {
    1745           0 :                     tok = expandHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens);
    1746             :                 } else {
    1747           0 :                     if (!expandArg(tokens, tok, rawloc, macros, expandedmacros, parametertokens)) {
    1748           0 :                         bool expanded = false;
    1749           0 :                         const MacroMap::const_iterator it = macros.find(tok->str());
    1750           0 :                         if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) {
    1751           0 :                             const Macro &m = it->second;
    1752           0 :                             if (!m.functionLike()) {
    1753           0 :                                 Token* mtok = tokens->back();
    1754           0 :                                 m.expand(tokens, rawloc, tok, macros, expandedmacros);
    1755           0 :                                 for (mtok = mtok->next; mtok; mtok = mtok->next) {
    1756           0 :                                     if (mtok->op == ',')
    1757           0 :                                         mtok->setstr(INNER_COMMA);
    1758             :                                 }
    1759           0 :                                 expanded = true;
    1760             :                             }
    1761             :                         }
    1762           0 :                         if (!expanded) {
    1763           0 :                             tokens->push_back(new Token(*tok));
    1764           0 :                             if (tok->macro.empty() && (par > 0 || tok->str() != "("))
    1765           0 :                                 tokens->back()->macro = name();
    1766             :                         }
    1767             :                     }
    1768             : 
    1769           0 :                     if (tok->op == '(')
    1770           0 :                         ++par;
    1771           0 :                     else if (tok->op == ')') {
    1772           0 :                         --par;
    1773           0 :                         if (par == 0U)
    1774           0 :                             break;
    1775             :                     }
    1776           0 :                     tok = tok->next;
    1777             :                 }
    1778             :             }
    1779           0 :             for (Token *tok2 = tokens->front(); tok2; tok2 = tok2->next)
    1780           0 :                 tok2->location = lpar->location;
    1781           0 :             return sameline(lpar,tok) ? tok : nullptr;
    1782             :         }
    1783             : 
    1784           0 :         const Token * expand(TokenList * const output, const Location &loc, const Token * const nameTokInst, const MacroMap &macros, std::set<TokenString> expandedmacros, bool first=false) const {
    1785             : 
    1786           0 :             if (!first)
    1787           0 :                 expandedmacros.insert(nameTokInst->str());
    1788             : 
    1789           0 :             usageList.push_back(loc);
    1790             : 
    1791           0 :             if (nameTokInst->str() == "__FILE__") {
    1792           0 :                 output->push_back(new Token('\"'+loc.file()+'\"', loc));
    1793           0 :                 return nameTokInst->next;
    1794             :             }
    1795           0 :             if (nameTokInst->str() == "__LINE__") {
    1796           0 :                 output->push_back(new Token(toString(loc.line), loc));
    1797           0 :                 return nameTokInst->next;
    1798             :             }
    1799           0 :             if (nameTokInst->str() == "__COUNTER__") {
    1800           0 :                 output->push_back(new Token(toString(usageList.size()-1U), loc));
    1801           0 :                 return nameTokInst->next;
    1802             :             }
    1803             : 
    1804           0 :             const bool calledInDefine = (loc.fileIndex != nameTokInst->location.fileIndex ||
    1805           0 :                                          loc.line < nameTokInst->location.line);
    1806             : 
    1807           0 :             std::vector<const Token*> parametertokens1(getMacroParameters(nameTokInst, calledInDefine));
    1808             : 
    1809           0 :             if (functionLike()) {
    1810             :                 // No arguments => not macro expansion
    1811           0 :                 if (nameTokInst->next && nameTokInst->next->op != '(') {
    1812           0 :                     output->push_back(new Token(nameTokInst->str(), loc));
    1813           0 :                     return nameTokInst->next;
    1814             :                 }
    1815             : 
    1816             :                 // Parse macro-call
    1817           0 :                 if (variadic) {
    1818           0 :                     if (parametertokens1.size() < args.size()) {
    1819           0 :                         throw wrongNumberOfParameters(nameTokInst->location, name());
    1820             :                     }
    1821             :                 } else {
    1822           0 :                     if (parametertokens1.size() != args.size() + (args.empty() ? 2U : 1U))
    1823           0 :                         throw wrongNumberOfParameters(nameTokInst->location, name());
    1824             :                 }
    1825             :             }
    1826             : 
    1827             :             // If macro call uses __COUNTER__ then expand that first
    1828           0 :             TokenList tokensparams(files);
    1829           0 :             std::vector<const Token *> parametertokens2;
    1830           0 :             if (!parametertokens1.empty()) {
    1831           0 :                 bool counter = false;
    1832           0 :                 for (const Token *tok = parametertokens1[0]; tok != parametertokens1.back(); tok = tok->next) {
    1833           0 :                     if (tok->str() == "__COUNTER__") {
    1834           0 :                         counter = true;
    1835           0 :                         break;
    1836             :                     }
    1837             :                 }
    1838             : 
    1839           0 :                 const MacroMap::const_iterator m = macros.find("__COUNTER__");
    1840             : 
    1841           0 :                 if (!counter || m == macros.end())
    1842           0 :                     parametertokens2.swap(parametertokens1);
    1843             :                 else {
    1844           0 :                     const Macro &counterMacro = m->second;
    1845           0 :                     unsigned int par = 0;
    1846           0 :                     for (const Token *tok = parametertokens1[0]; tok && par < parametertokens1.size(); tok = tok->next) {
    1847           0 :                         if (tok->str() == "__COUNTER__") {
    1848           0 :                             tokensparams.push_back(new Token(toString(counterMacro.usageList.size()), tok->location));
    1849           0 :                             counterMacro.usageList.push_back(tok->location);
    1850             :                         } else {
    1851           0 :                             tokensparams.push_back(new Token(*tok));
    1852           0 :                             if (tok == parametertokens1[par]) {
    1853           0 :                                 parametertokens2.push_back(tokensparams.cback());
    1854           0 :                                 par++;
    1855             :                             }
    1856             :                         }
    1857             :                     }
    1858             :                 }
    1859             :             }
    1860             : 
    1861           0 :             Token * const output_end_1 = output->back();
    1862             : 
    1863             :             // expand
    1864           0 :             for (const Token *tok = valueToken; tok != endToken;) {
    1865           0 :                 if (tok->op != '#') {
    1866             :                     // A##B => AB
    1867           0 :                     if (sameline(tok, tok->next) && tok->next && tok->next->op == '#' && tok->next->next && tok->next->next->op == '#') {
    1868           0 :                         if (!sameline(tok, tok->next->next->next))
    1869           0 :                             throw invalidHashHash::unexpectedNewline(tok->location, name());
    1870           0 :                         TokenList new_output(files);
    1871           0 :                         if (!expandArg(&new_output, tok, parametertokens2))
    1872           0 :                             output->push_back(newMacroToken(tok->str(), loc, isReplaced(expandedmacros), tok));
    1873           0 :                         else if (new_output.empty()) // placemarker token
    1874           0 :                             output->push_back(newMacroToken("", loc, isReplaced(expandedmacros)));
    1875             :                         else
    1876           0 :                             for (const Token *tok2 = new_output.cfront(); tok2; tok2 = tok2->next)
    1877           0 :                                 output->push_back(newMacroToken(tok2->str(), loc, isReplaced(expandedmacros), tok2));
    1878           0 :                         tok = tok->next;
    1879             :                     } else {
    1880           0 :                         tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens2);
    1881             :                     }
    1882           0 :                     continue;
    1883             :                 }
    1884             : 
    1885           0 :                 int numberOfHash = 1;
    1886           0 :                 const Token *hashToken = tok->next;
    1887           0 :                 while (sameline(tok,hashToken) && hashToken->op == '#') {
    1888           0 :                     hashToken = hashToken->next;
    1889           0 :                     ++numberOfHash;
    1890             :                 }
    1891           0 :                 if (numberOfHash == 4 && tok->next->location.col + 1 == tok->next->next->location.col) {
    1892             :                     // # ## #  => ##
    1893           0 :                     output->push_back(newMacroToken("##", loc, isReplaced(expandedmacros)));
    1894           0 :                     tok = hashToken;
    1895           0 :                     continue;
    1896             :                 }
    1897             : 
    1898           0 :                 if (numberOfHash >= 2 && tok->location.col + 1 < tok->next->location.col) {
    1899           0 :                     output->push_back(new Token(*tok));
    1900           0 :                     tok = tok->next;
    1901           0 :                     continue;
    1902             :                 }
    1903             : 
    1904           0 :                 tok = tok->next;
    1905           0 :                 if (tok == endToken) {
    1906           0 :                     output->push_back(new Token(*tok->previous));
    1907           0 :                     break;
    1908             :                 }
    1909           0 :                 if (tok->op == '#') {
    1910             :                     // A##B => AB
    1911           0 :                     tok = expandHashHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2);
    1912             :                 } else {
    1913             :                     // #123 => "123"
    1914           0 :                     tok = expandHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2);
    1915             :                 }
    1916             :             }
    1917             : 
    1918           0 :             if (!functionLike()) {
    1919           0 :                 for (Token *tok = output_end_1 ? output_end_1->next : output->front(); tok; tok = tok->next) {
    1920           0 :                     tok->macro = nameTokInst->str();
    1921             :                 }
    1922             :             }
    1923             : 
    1924           0 :             if (!parametertokens1.empty())
    1925           0 :                 parametertokens1.swap(parametertokens2);
    1926             : 
    1927           0 :             return functionLike() ? parametertokens2.back()->next : nameTokInst->next;
    1928             :         }
    1929             : 
    1930           0 :         const Token *recursiveExpandToken(TokenList *output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
    1931           0 :             if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) {
    1932           0 :                 output->takeTokens(temp);
    1933           0 :                 return tok->next;
    1934             :             }
    1935             : 
    1936           0 :             if (!sameline(tok, tok->next)) {
    1937           0 :                 output->takeTokens(temp);
    1938           0 :                 return tok->next;
    1939             :             }
    1940             : 
    1941           0 :             const MacroMap::const_iterator it = macros.find(temp.cback()->str());
    1942           0 :             if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) {
    1943           0 :                 output->takeTokens(temp);
    1944           0 :                 return tok->next;
    1945             :             }
    1946             : 
    1947           0 :             const Macro &calledMacro = it->second;
    1948           0 :             if (!calledMacro.functionLike()) {
    1949           0 :                 output->takeTokens(temp);
    1950           0 :                 return tok->next;
    1951             :             }
    1952             : 
    1953           0 :             TokenList temp2(files);
    1954           0 :             temp2.push_back(new Token(temp.cback()->str(), tok->location));
    1955             : 
    1956           0 :             const Token * const tok2 = appendTokens(&temp2, loc, tok->next, macros, expandedmacros, parametertokens);
    1957           0 :             if (!tok2)
    1958           0 :                 return tok->next;
    1959           0 :             output->takeTokens(temp);
    1960           0 :             output->deleteToken(output->back());
    1961           0 :             calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros);
    1962           0 :             return tok2->next;
    1963             :         }
    1964             : 
    1965           0 :         const Token *expandToken(TokenList *output, const Location &loc, const Token *tok, const MacroMap &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
    1966             :             // Not name..
    1967           0 :             if (!tok->name) {
    1968           0 :                 output->push_back(newMacroToken(tok->str(), loc, true, tok));
    1969           0 :                 return tok->next;
    1970             :             }
    1971             : 
    1972             :             // Macro parameter..
    1973             :             {
    1974           0 :                 TokenList temp(files);
    1975           0 :                 if (tok->str() == "__VA_OPT__") {
    1976           0 :                     if (sameline(tok, tok->next) && tok->next->str() == "(") {
    1977           0 :                         tok = tok->next;
    1978           0 :                         int paren = 1;
    1979           0 :                         while (sameline(tok, tok->next)) {
    1980           0 :                             if (tok->next->str() == "(")
    1981           0 :                               ++paren;
    1982           0 :                             else if (tok->next->str() == ")")
    1983           0 :                               --paren;                            
    1984           0 :                             if (paren == 0)
    1985           0 :                               return tok->next->next;
    1986           0 :                             tok = tok->next;
    1987           0 :                             if (parametertokens.size() > args.size() && parametertokens.front()->next->str() != ")")
    1988           0 :                               tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens)->previous;
    1989             :                         }
    1990             :                     }
    1991           0 :                     throw Error(tok->location, "Missing parenthesis for __VA_OPT__(content)");
    1992             :                 }
    1993           0 :                 if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) {
    1994           0 :                     if (tok->str() == "__VA_ARGS__" && temp.empty() && output->cback() && output->cback()->str() == "," &&
    1995           0 :                         tok->nextSkipComments() && tok->nextSkipComments()->str() == ")")
    1996           0 :                         output->deleteToken(output->back());
    1997           0 :                     return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros, parametertokens);
    1998             :                 }
    1999             :             }
    2000             : 
    2001             :             // Macro..
    2002           0 :             const MacroMap::const_iterator it = macros.find(tok->str());
    2003           0 :             if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) {
    2004           0 :                 std::set<std::string> expandedmacros2(expandedmacros);
    2005           0 :                 expandedmacros2.insert(tok->str());
    2006             : 
    2007           0 :                 const Macro &calledMacro = it->second;
    2008           0 :                 if (!calledMacro.functionLike()) {
    2009           0 :                     TokenList temp(files);
    2010           0 :                     calledMacro.expand(&temp, loc, tok, macros, expandedmacros);
    2011           0 :                     return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros2, parametertokens);
    2012             :                 }
    2013           0 :                 if (!sameline(tok, tok->next) || tok->next->op != '(') {
    2014           0 :                     output->push_back(newMacroToken(tok->str(), loc, true, tok));
    2015           0 :                     return tok->next;
    2016             :                 }
    2017           0 :                 TokenList tokens(files);
    2018           0 :                 tokens.push_back(new Token(*tok));
    2019           0 :                 const Token * const tok2 = appendTokens(&tokens, loc, tok->next, macros, expandedmacros, parametertokens);
    2020           0 :                 if (!tok2) {
    2021           0 :                     output->push_back(newMacroToken(tok->str(), loc, true, tok));
    2022           0 :                     return tok->next;
    2023             :                 }
    2024           0 :                 TokenList temp(files);
    2025           0 :                 calledMacro.expand(&temp, loc, tokens.cfront(), macros, expandedmacros);
    2026           0 :                 return recursiveExpandToken(output, temp, loc, tok2, macros, expandedmacros2, parametertokens);
    2027             :             }
    2028             : 
    2029           0 :             if (tok->str() == DEFINED) {
    2030           0 :                 const Token * const tok2 = tok->next;
    2031           0 :                 const Token * const tok3 = tok2 ? tok2->next : nullptr;
    2032           0 :                 const Token * const tok4 = tok3 ? tok3->next : nullptr;
    2033           0 :                 const Token *defToken = nullptr;
    2034           0 :                 const Token *lastToken = nullptr;
    2035           0 :                 if (sameline(tok, tok4) && tok2->op == '(' && tok3->name && tok4->op == ')') {
    2036           0 :                     defToken = tok3;
    2037           0 :                     lastToken = tok4;
    2038           0 :                 } else if (sameline(tok,tok2) && tok2->name) {
    2039           0 :                     defToken = lastToken = tok2;
    2040             :                 }
    2041           0 :                 if (defToken) {
    2042           0 :                     std::string macroName = defToken->str();
    2043           0 :                     if (defToken->next && defToken->next->op == '#' && defToken->next->next && defToken->next->next->op == '#' && defToken->next->next->next && defToken->next->next->next->name && sameline(defToken,defToken->next->next->next)) {
    2044           0 :                         TokenList temp(files);
    2045           0 :                         if (expandArg(&temp, defToken, parametertokens))
    2046           0 :                             macroName = temp.cback()->str();
    2047           0 :                         if (expandArg(&temp, defToken->next->next->next, parametertokens))
    2048           0 :                             macroName += temp.cback()->str();
    2049             :                         else
    2050           0 :                             macroName += defToken->next->next->next->str();
    2051           0 :                         lastToken = defToken->next->next->next;
    2052             :                     }
    2053           0 :                     const bool def = (macros.find(macroName) != macros.end());
    2054           0 :                     output->push_back(newMacroToken(def ? "1" : "0", loc, true));
    2055           0 :                     return lastToken->next;
    2056             :                 }
    2057             :             }
    2058             : 
    2059           0 :             output->push_back(newMacroToken(tok->str(), loc, true, tok));
    2060           0 :             return tok->next;
    2061             :         }
    2062             : 
    2063           0 :         bool expandArg(TokenList *output, const Token *tok, const std::vector<const Token*> &parametertokens) const {
    2064           0 :             if (!tok->name)
    2065           0 :                 return false;
    2066             : 
    2067           0 :             const unsigned int argnr = getArgNum(tok->str());
    2068           0 :             if (argnr >= args.size())
    2069           0 :                 return false;
    2070             : 
    2071             :             // empty variadic parameter
    2072           0 :             if (variadic && argnr + 1U >= parametertokens.size())
    2073           0 :                 return true;
    2074             : 
    2075           0 :             for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U]; partok = partok->next)
    2076           0 :                 output->push_back(new Token(*partok));
    2077             : 
    2078           0 :             return true;
    2079             :         }
    2080             : 
    2081           0 :         bool expandArg(TokenList *output, const Token *tok, const Location &loc, const MacroMap &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
    2082           0 :             if (!tok->name)
    2083           0 :                 return false;
    2084           0 :             const unsigned int argnr = getArgNum(tok->str());
    2085           0 :             if (argnr >= args.size())
    2086           0 :                 return false;
    2087           0 :             if (variadic && argnr + 1U >= parametertokens.size()) // empty variadic parameter
    2088           0 :                 return true;
    2089           0 :             for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U];) {
    2090           0 :                 const MacroMap::const_iterator it = macros.find(partok->str());
    2091           0 :                 if (it != macros.end() && !partok->isExpandedFrom(&it->second) && (partok->str() == name() || expandedmacros.find(partok->str()) == expandedmacros.end()))
    2092           0 :                     partok = it->second.expand(output, loc, partok, macros, expandedmacros);
    2093             :                 else {
    2094           0 :                     output->push_back(newMacroToken(partok->str(), loc, isReplaced(expandedmacros), partok));
    2095           0 :                     output->back()->macro = partok->macro;
    2096           0 :                     partok = partok->next;
    2097             :                 }
    2098             :             }
    2099           0 :             return true;
    2100             :         }
    2101             : 
    2102             :         /**
    2103             :          * Expand #X => "X"
    2104             :          * @param output  destination tokenlist
    2105             :          * @param loc     location for expanded token
    2106             :          * @param tok     The # token
    2107             :          * @param macros  all macros
    2108             :          * @param expandedmacros   set with expanded macros, with this macro
    2109             :          * @param parametertokens  parameters given when expanding this macro
    2110             :          * @return token after the X
    2111             :          */
    2112           0 :         const Token *expandHash(TokenList *output, const Location &loc, const Token *tok, const MacroMap &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
    2113           0 :             TokenList tokenListHash(files);
    2114           0 :             tok = expandToken(&tokenListHash, loc, tok->next, macros, expandedmacros, parametertokens);
    2115           0 :             std::ostringstream ostr;
    2116           0 :             ostr << '\"';
    2117           0 :             for (const Token *hashtok = tokenListHash.cfront(); hashtok; hashtok = hashtok->next)
    2118           0 :                 ostr << hashtok->str();
    2119           0 :             ostr << '\"';
    2120           0 :             output->push_back(newMacroToken(escapeString(ostr.str()), loc, isReplaced(expandedmacros)));
    2121           0 :             return tok;
    2122             :         }
    2123             : 
    2124             :         /**
    2125             :          * Expand A##B => AB
    2126             :          * The A should already be expanded. Call this when you reach the first # token
    2127             :          * @param output  destination tokenlist
    2128             :          * @param loc     location for expanded token
    2129             :          * @param tok     first # token
    2130             :          * @param macros  all macros
    2131             :          * @param expandedmacros   set with expanded macros, with this macro
    2132             :          * @param parametertokens  parameters given when expanding this macro
    2133             :          * @return token after B
    2134             :          */
    2135           0 :         const Token *expandHashHash(TokenList *output, const Location &loc, const Token *tok, const MacroMap &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
    2136           0 :             Token *A = output->back();
    2137           0 :             if (!A)
    2138           0 :                 throw invalidHashHash(tok->location, name(), "Missing first argument");
    2139           0 :             if (!sameline(tok, tok->next) || !sameline(tok, tok->next->next))
    2140           0 :                 throw invalidHashHash::unexpectedNewline(tok->location, name());
    2141             : 
    2142           0 :             const bool canBeConcatenatedWithEqual = A->isOneOf("+-*/%&|^") || A->str() == "<<" || A->str() == ">>";
    2143           0 :             const bool canBeConcatenatedStringOrChar = isStringLiteral_(A->str()) || isCharLiteral_(A->str());
    2144           0 :             if (!A->name && !A->number && A->op != ',' && !A->str().empty() && !canBeConcatenatedWithEqual && !canBeConcatenatedStringOrChar)
    2145           0 :                 throw invalidHashHash::unexpectedToken(tok->location, name(), A);
    2146             : 
    2147           0 :             Token * const B = tok->next->next;
    2148           0 :             if (!B->name && !B->number && B->op && !B->isOneOf("#="))
    2149           0 :                 throw invalidHashHash::unexpectedToken(tok->location, name(), B);
    2150             : 
    2151           0 :             if ((canBeConcatenatedWithEqual && B->op != '=') ||
    2152           0 :                 (!canBeConcatenatedWithEqual && B->op == '='))
    2153           0 :                 throw invalidHashHash::cannotCombine(tok->location, name(), A, B);
    2154             : 
    2155             :             // Superficial check; more in-depth would in theory be possible _after_ expandArg
    2156           0 :             if (canBeConcatenatedStringOrChar && (B->number || !B->name))
    2157           0 :                 throw invalidHashHash::cannotCombine(tok->location, name(), A, B);
    2158             : 
    2159           0 :             TokenList tokensB(files);
    2160           0 :             const Token *nextTok = B->next;
    2161             : 
    2162           0 :             if (canBeConcatenatedStringOrChar) {
    2163             :                 // It seems clearer to handle this case separately even though the code is similar-ish, but we don't want to merge here.
    2164             :                 // TODO The question is whether the ## or varargs may still apply, and how to provoke?
    2165           0 :                 if (expandArg(&tokensB, B, parametertokens)) {
    2166           0 :                     for (Token *b = tokensB.front(); b; b = b->next)
    2167           0 :                         b->location = loc;
    2168             :                 } else {
    2169           0 :                     tokensB.push_back(new Token(*B));
    2170           0 :                     tokensB.back()->location = loc;
    2171             :                 }
    2172           0 :                 output->takeTokens(tokensB);
    2173             :             } else {
    2174           0 :                 std::string strAB;
    2175             : 
    2176           0 :                 const bool varargs = variadic && !args.empty() && B->str() == args[args.size()-1U];
    2177             : 
    2178           0 :                 if (expandArg(&tokensB, B, parametertokens)) {
    2179           0 :                     if (tokensB.empty())
    2180           0 :                         strAB = A->str();
    2181           0 :                     else if (varargs && A->op == ',') {
    2182           0 :                         strAB = ",";
    2183             :                     } else {
    2184           0 :                         strAB = A->str() + tokensB.cfront()->str();
    2185           0 :                         tokensB.deleteToken(tokensB.front());
    2186             :                     }
    2187             :                 } else {
    2188           0 :                     strAB = A->str() + B->str();
    2189             :                 }
    2190             : 
    2191             :                 // producing universal character is undefined behavior
    2192           0 :                 if (A->previous && A->previous->str() == "\\") {
    2193           0 :                     if (strAB[0] == 'u' && strAB.size() == 5)
    2194           0 :                         throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB);
    2195           0 :                     if (strAB[0] == 'U' && strAB.size() == 9)
    2196           0 :                         throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB);
    2197             :                 }
    2198             : 
    2199           0 :                 if (varargs && tokensB.empty() && tok->previous->str() == ",")
    2200           0 :                     output->deleteToken(A);
    2201           0 :                 else if (strAB != "," && macros.find(strAB) == macros.end()) {
    2202           0 :                     A->setstr(strAB);
    2203           0 :                     for (Token *b = tokensB.front(); b; b = b->next)
    2204           0 :                         b->location = loc;
    2205           0 :                     output->takeTokens(tokensB);
    2206           0 :                 } else if (sameline(B, nextTok) && sameline(B, nextTok->next) && nextTok->op == '#' && nextTok->next->op == '#') {
    2207           0 :                     TokenList output2(files);
    2208           0 :                     output2.push_back(new Token(strAB, tok->location));
    2209           0 :                     nextTok = expandHashHash(&output2, loc, nextTok, macros, expandedmacros, parametertokens);
    2210           0 :                     output->deleteToken(A);
    2211           0 :                     output->takeTokens(output2);
    2212             :                 } else {
    2213           0 :                     output->deleteToken(A);
    2214           0 :                     TokenList tokens(files);
    2215           0 :                     tokens.push_back(new Token(strAB, tok->location));
    2216             :                     // for function like macros, push the (...)
    2217           0 :                     if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') {
    2218           0 :                         const MacroMap::const_iterator it = macros.find(strAB);
    2219           0 :                         if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) {
    2220           0 :                             const Token * const tok2 = appendTokens(&tokens, loc, B->next, macros, expandedmacros, parametertokens);
    2221           0 :                             if (tok2)
    2222           0 :                                 nextTok = tok2->next;
    2223             :                         }
    2224             :                     }
    2225           0 :                     expandToken(output, loc, tokens.cfront(), macros, expandedmacros, parametertokens);
    2226           0 :                     for (Token *b = tokensB.front(); b; b = b->next)
    2227           0 :                         b->location = loc;
    2228           0 :                     output->takeTokens(tokensB);
    2229             :                 }
    2230             :             }
    2231             : 
    2232           0 :             return nextTok;
    2233             :         }
    2234             : 
    2235           0 :         static bool isReplaced(const std::set<std::string> &expandedmacros) {
    2236             :             // return true if size > 1
    2237           0 :             std::set<std::string>::const_iterator it = expandedmacros.begin();
    2238           0 :             if (it == expandedmacros.end())
    2239           0 :                 return false;
    2240           0 :             ++it;
    2241           0 :             return (it != expandedmacros.end());
    2242             :         }
    2243             : 
    2244             :         /** name token in definition */
    2245             :         const Token *nameTokDef;
    2246             : 
    2247             :         /** arguments for macro */
    2248             :         std::vector<TokenString> args;
    2249             : 
    2250             :         /** first token in replacement string */
    2251             :         const Token *valueToken;
    2252             : 
    2253             :         /** token after replacement string */
    2254             :         const Token *endToken;
    2255             : 
    2256             :         /** files */
    2257             :         std::vector<std::string> &files;
    2258             : 
    2259             :         /** this is used for -D where the definition is not seen anywhere in code */
    2260             :         TokenList tokenListDefine;
    2261             : 
    2262             :         /** usage of this macro */
    2263             :         mutable std::list<Location> usageList;
    2264             : 
    2265             :         /** is macro variadic? */
    2266             :         bool variadic;
    2267             : 
    2268             :         /** was the value of this macro actually defined in the code? */
    2269             :         bool valueDefinedInCode_;
    2270             :     };
    2271             : }
    2272             : 
    2273             : namespace simplecpp {
    2274             : 
    2275             : #ifdef __CYGWIN__
    2276             :     bool startsWith(const std::string &str, const std::string &s)
    2277             :     {
    2278             :         return (str.size() >= s.size() && str.compare(0, s.size(), s) == 0);
    2279             :     }
    2280             : 
    2281             :     std::string convertCygwinToWindowsPath(const std::string &cygwinPath)
    2282             :     {
    2283             :         std::string windowsPath;
    2284             : 
    2285             :         std::string::size_type pos = 0;
    2286             :         if (cygwinPath.size() >= 11 && startsWith(cygwinPath, "/cygdrive/")) {
    2287             :             const unsigned char driveLetter = cygwinPath[10];
    2288             :             if (std::isalpha(driveLetter)) {
    2289             :                 if (cygwinPath.size() == 11) {
    2290             :                     windowsPath = toupper(driveLetter);
    2291             :                     windowsPath += ":\\";   // volume root directory
    2292             :                     pos = 11;
    2293             :                 } else if (cygwinPath[11] == '/') {
    2294             :                     windowsPath = toupper(driveLetter);
    2295             :                     windowsPath += ":";
    2296             :                     pos = 11;
    2297             :                 }
    2298             :             }
    2299             :         }
    2300             : 
    2301             :         for (; pos < cygwinPath.size(); ++pos) {
    2302             :             unsigned char c = cygwinPath[pos];
    2303             :             if (c == '/')
    2304             :                 c = '\\';
    2305             :             windowsPath += c;
    2306             :         }
    2307             : 
    2308             :         return windowsPath;
    2309             :     }
    2310             : #endif
    2311             : }
    2312             : 
    2313             : #ifdef SIMPLECPP_WINDOWS
    2314             : 
    2315             : #if __cplusplus >= 201103L
    2316             : using MyMutex = std::mutex;
    2317             : template<class T>
    2318             : using MyLock = std::lock_guard<T>;
    2319             : #else
    2320             : class MyMutex {
    2321             : public:
    2322             :     MyMutex() {
    2323             :         InitializeCriticalSection(&m_criticalSection);
    2324             :     }
    2325             : 
    2326             :     ~MyMutex() {
    2327             :         DeleteCriticalSection(&m_criticalSection);
    2328             :     }
    2329             : 
    2330             :     CRITICAL_SECTION* lock() {
    2331             :         return &m_criticalSection;
    2332             :     }
    2333             : private:
    2334             :     CRITICAL_SECTION m_criticalSection;
    2335             : };
    2336             : 
    2337             : template<typename T>
    2338             : class MyLock {
    2339             : public:
    2340             :     explicit MyLock(T& m)
    2341             :         : m_mutex(m) {
    2342             :         EnterCriticalSection(m_mutex.lock());
    2343             :     }
    2344             : 
    2345             :     ~MyLock() {
    2346             :         LeaveCriticalSection(m_mutex.lock());
    2347             :     }
    2348             : 
    2349             : private:
    2350             :     MyLock& operator=(const MyLock&);
    2351             :     MyLock(const MyLock&);
    2352             : 
    2353             :     T& m_mutex;
    2354             : };
    2355             : #endif
    2356             : 
    2357             : class RealFileNameMap {
    2358             : public:
    2359             :     RealFileNameMap() {}
    2360             : 
    2361             :     bool getCacheEntry(const std::string& path, std::string& returnPath) {
    2362             :         MyLock<MyMutex> lock(m_mutex);
    2363             : 
    2364             :         const std::map<std::string, std::string>::iterator it = m_fileMap.find(path);
    2365             :         if (it != m_fileMap.end()) {
    2366             :             returnPath = it->second;
    2367             :             return true;
    2368             :         }
    2369             :         return false;
    2370             :     }
    2371             : 
    2372             :     void addToCache(const std::string& path, const std::string& actualPath) {
    2373             :         MyLock<MyMutex> lock(m_mutex);
    2374             :         m_fileMap[path] = actualPath;
    2375             :     }
    2376             : 
    2377             : private:
    2378             :     std::map<std::string, std::string> m_fileMap;
    2379             :     MyMutex m_mutex;
    2380             : };
    2381             : 
    2382             : static RealFileNameMap realFileNameMap;
    2383             : 
    2384             : static bool realFileName(const std::string &f, std::string &result)
    2385             : {
    2386             :     // are there alpha characters in last subpath?
    2387             :     bool alpha = false;
    2388             :     for (std::string::size_type pos = 1; pos <= f.size(); ++pos) {
    2389             :         const unsigned char c = f[f.size() - pos];
    2390             :         if (c == '/' || c == '\\')
    2391             :             break;
    2392             :         if (std::isalpha(c)) {
    2393             :             alpha = true;
    2394             :             break;
    2395             :         }
    2396             :     }
    2397             : 
    2398             :     // do not convert this path if there are no alpha characters (either pointless or cause wrong results for . and ..)
    2399             :     if (!alpha)
    2400             :         return false;
    2401             : 
    2402             :     // Lookup filename or foldername on file system
    2403             :     if (!realFileNameMap.getCacheEntry(f, result)) {
    2404             : 
    2405             :         WIN32_FIND_DATAA FindFileData;
    2406             : 
    2407             : #ifdef __CYGWIN__
    2408             :         const std::string fConverted = simplecpp::convertCygwinToWindowsPath(f);
    2409             :         const HANDLE hFind = FindFirstFileExA(fConverted.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0);
    2410             : #else
    2411             :         HANDLE hFind = FindFirstFileExA(f.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0);
    2412             : #endif
    2413             : 
    2414             :         if (INVALID_HANDLE_VALUE == hFind)
    2415             :             return false;
    2416             :         result = FindFileData.cFileName;
    2417             :         realFileNameMap.addToCache(f, result);
    2418             :         FindClose(hFind);
    2419             :     }
    2420             :     return true;
    2421             : }
    2422             : 
    2423             : static RealFileNameMap realFilePathMap;
    2424             : 
    2425             : /** Change case in given path to match filesystem */
    2426             : static std::string realFilename(const std::string &f)
    2427             : {
    2428             :     std::string ret;
    2429             :     ret.reserve(f.size()); // this will be the final size
    2430             :     if (realFilePathMap.getCacheEntry(f, ret))
    2431             :         return ret;
    2432             : 
    2433             :     // Current subpath
    2434             :     std::string subpath;
    2435             : 
    2436             :     for (std::string::size_type pos = 0; pos < f.size(); ++pos) {
    2437             :         const unsigned char c = f[pos];
    2438             : 
    2439             :         // Separator.. add subpath and separator
    2440             :         if (c == '/' || c == '\\') {
    2441             :             // if subpath is empty just add separator
    2442             :             if (subpath.empty()) {
    2443             :                 ret += c;
    2444             :                 continue;
    2445             :             }
    2446             : 
    2447             :             const bool isDriveSpecification =
    2448             :                 (pos == 2 && subpath.size() == 2 && std::isalpha(subpath[0]) && subpath[1] == ':');
    2449             : 
    2450             :             // Append real filename (proper case)
    2451             :             std::string f2;
    2452             :             if (!isDriveSpecification && realFileName(f.substr(0, pos), f2))
    2453             :                 ret += f2;
    2454             :             else
    2455             :                 ret += subpath;
    2456             : 
    2457             :             subpath.clear();
    2458             : 
    2459             :             // Append separator
    2460             :             ret += c;
    2461             :         } else {
    2462             :             subpath += c;
    2463             :         }
    2464             :     }
    2465             : 
    2466             :     if (!subpath.empty()) {
    2467             :         std::string f2;
    2468             :         if (realFileName(f,f2))
    2469             :             ret += f2;
    2470             :         else
    2471             :             ret += subpath;
    2472             :     }
    2473             : 
    2474             :     realFilePathMap.addToCache(f, ret);
    2475             :     return ret;
    2476             : }
    2477             : 
    2478             : static bool isAbsolutePath(const std::string &path)
    2479             : {
    2480             :     if (path.length() >= 3 && path[0] > 0 && std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/'))
    2481             :         return true;
    2482             :     return path.length() > 1U && (path[0] == '/' || path[0] == '\\');
    2483             : }
    2484             : #else
    2485             : #define realFilename(f)  f
    2486             : 
    2487           0 : static bool isAbsolutePath(const std::string &path)
    2488             : {
    2489           0 :     return path.length() > 1U && path[0] == '/';
    2490             : }
    2491             : #endif
    2492             : 
    2493             : namespace simplecpp {
    2494             :     /**
    2495             :      * perform path simplifications for . and ..
    2496             :      */
    2497           0 :     std::string simplifyPath(std::string path)
    2498             :     {
    2499           0 :         if (path.empty())
    2500           0 :             return path;
    2501             : 
    2502             :         std::string::size_type pos;
    2503             : 
    2504             :         // replace backslash separators
    2505           0 :         std::replace(path.begin(), path.end(), '\\', '/');
    2506             : 
    2507           0 :         const bool unc(path.compare(0,2,"//") == 0);
    2508             : 
    2509             :         // replace "//" with "/"
    2510           0 :         pos = 0;
    2511           0 :         while ((pos = path.find("//",pos)) != std::string::npos) {
    2512           0 :             path.erase(pos,1);
    2513             :         }
    2514             : 
    2515             :         // remove "./"
    2516           0 :         pos = 0;
    2517           0 :         while ((pos = path.find("./",pos)) != std::string::npos) {
    2518           0 :             if (pos == 0 || path[pos - 1U] == '/')
    2519           0 :                 path.erase(pos,2);
    2520             :             else
    2521           0 :                 pos += 2;
    2522             :         }
    2523             : 
    2524             :         // remove trailing dot if path ends with "/."
    2525           0 :         if (endsWith(path,"/."))
    2526           0 :             path.erase(path.size()-1);
    2527             : 
    2528             :         // simplify ".."
    2529           0 :         pos = 1; // don't simplify ".." if path starts with that
    2530           0 :         while ((pos = path.find("/..", pos)) != std::string::npos) {
    2531             :             // not end of path, then string must be "/../"
    2532           0 :             if (pos + 3 < path.size() && path[pos + 3] != '/') {
    2533           0 :                 ++pos;
    2534           0 :                 continue;
    2535             :             }
    2536             :             // get previous subpath
    2537           0 :             std::string::size_type pos1 = path.rfind('/', pos - 1U);
    2538           0 :             if (pos1 == std::string::npos) {
    2539           0 :                 pos1 = 0;
    2540             :             } else {
    2541           0 :                 pos1 += 1U;
    2542             :             }
    2543           0 :             const std::string previousSubPath = path.substr(pos1, pos - pos1);
    2544           0 :             if (previousSubPath == "..") {
    2545             :                 // don't simplify
    2546           0 :                 ++pos;
    2547             :             } else {
    2548             :                 // remove previous subpath and ".."
    2549           0 :                 path.erase(pos1, pos - pos1 + 4);
    2550           0 :                 if (path.empty())
    2551           0 :                     path = ".";
    2552             :                 // update pos
    2553           0 :                 pos = (pos1 == 0) ? 1 : (pos1 - 1);
    2554             :             }
    2555             :         }
    2556             : 
    2557             :         // Remove trailing '/'?
    2558             :         //if (path.size() > 1 && endsWith(path, "/"))
    2559             :         //    path.erase(path.size()-1);
    2560             : 
    2561           0 :         if (unc)
    2562           0 :             path = '/' + path;
    2563             : 
    2564             :         // cppcheck-suppress duplicateExpressionTernary - platform-dependent implementation
    2565           0 :         return strpbrk(path.c_str(), "*?") == nullptr ? realFilename(path) : path;
    2566             :     }
    2567             : }
    2568             : 
    2569             : /** Evaluate sizeof(type) */
    2570           0 : static void simplifySizeof(simplecpp::TokenList &expr, const std::map<std::string, std::size_t> &sizeOfType)
    2571             : {
    2572           0 :     for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
    2573           0 :         if (tok->str() != "sizeof")
    2574           0 :             continue;
    2575           0 :         simplecpp::Token *tok1 = tok->next;
    2576           0 :         if (!tok1) {
    2577           0 :             throw std::runtime_error("missing sizeof argument");
    2578             :         }
    2579           0 :         simplecpp::Token *tok2 = tok1->next;
    2580           0 :         if (!tok2) {
    2581           0 :             throw std::runtime_error("missing sizeof argument");
    2582             :         }
    2583           0 :         if (tok1->op == '(') {
    2584           0 :             tok1 = tok1->next;
    2585           0 :             while (tok2->op != ')') {
    2586           0 :                 tok2 = tok2->next;
    2587           0 :                 if (!tok2) {
    2588           0 :                     throw std::runtime_error("invalid sizeof expression");
    2589             :                 }
    2590             :             }
    2591             :         }
    2592             : 
    2593           0 :         std::string type;
    2594           0 :         for (simplecpp::Token *typeToken = tok1; typeToken != tok2; typeToken = typeToken->next) {
    2595           0 :             if ((typeToken->str() == "unsigned" || typeToken->str() == "signed") && typeToken->next->name)
    2596           0 :                 continue;
    2597           0 :             if (typeToken->str() == "*" && type.find('*') != std::string::npos)
    2598           0 :                 continue;
    2599           0 :             if (!type.empty())
    2600           0 :                 type += ' ';
    2601           0 :             type += typeToken->str();
    2602             :         }
    2603             : 
    2604           0 :         const std::map<std::string, std::size_t>::const_iterator it = sizeOfType.find(type);
    2605           0 :         if (it != sizeOfType.end())
    2606           0 :             tok->setstr(toString(it->second));
    2607             :         else
    2608           0 :             continue;
    2609             : 
    2610           0 :         tok2 = tok2->next;
    2611           0 :         while (tok->next != tok2)
    2612           0 :             expr.deleteToken(tok->next);
    2613             :     }
    2614           0 : }
    2615             : 
    2616             : /** Evaluate __has_include(file) */
    2617             : static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader);
    2618           0 : static void simplifyHasInclude(simplecpp::TokenList &expr, const simplecpp::DUI &dui)
    2619             : {
    2620           0 :     for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
    2621           0 :         if (tok->str() != "__has_include")
    2622           0 :             continue;
    2623           0 :         simplecpp::Token *tok1 = tok->next;
    2624           0 :         if (!tok1) {
    2625           0 :             throw std::runtime_error("missing __has_include argument");
    2626             :         }
    2627           0 :         simplecpp::Token *tok2 = tok1->next;
    2628           0 :         if (!tok2) {
    2629           0 :             throw std::runtime_error("missing __has_include argument");
    2630             :         }
    2631           0 :         if (tok1->op == '(') {
    2632           0 :             tok1 = tok1->next;
    2633           0 :             while (tok2->op != ')') {
    2634           0 :                 tok2 = tok2->next;
    2635           0 :                 if (!tok2) {
    2636           0 :                     throw std::runtime_error("invalid __has_include expression");
    2637             :                 }
    2638             :             }
    2639             :         }
    2640             : 
    2641           0 :         const std::string &sourcefile = tok->location.file();
    2642           0 :         const bool systemheader = (tok1 && tok1->op == '<');
    2643           0 :         std::string header;
    2644           0 :         if (systemheader) {
    2645           0 :             simplecpp::Token *tok3 = tok1->next;
    2646           0 :             if (!tok3) {
    2647           0 :                 throw std::runtime_error("missing __has_include closing angular bracket");
    2648             :             }
    2649           0 :             while (tok3->op != '>') {
    2650           0 :                 tok3 = tok3->next;
    2651           0 :                 if (!tok3) {
    2652           0 :                     throw std::runtime_error("invalid __has_include expression");
    2653             :                 }
    2654             :             }
    2655             : 
    2656           0 :             for (simplecpp::Token *headerToken = tok1->next; headerToken != tok3; headerToken = headerToken->next)
    2657           0 :                 header += headerToken->str();
    2658             :             // cppcheck-suppress selfAssignment - platform-dependent implementation
    2659           0 :             header = realFilename(header);
    2660             :         }
    2661             :         else {
    2662           0 :             header = realFilename(tok1->str().substr(1U, tok1->str().size() - 2U));
    2663             :         }
    2664           0 :         std::ifstream f;
    2665           0 :         const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader);
    2666           0 :         tok->setstr(header2.empty() ? "0" : "1");
    2667             : 
    2668           0 :         tok2 = tok2->next;
    2669           0 :         while (tok->next != tok2)
    2670           0 :             expr.deleteToken(tok->next);
    2671             :     }
    2672           0 : }
    2673             : 
    2674             : static const char * const altopData[] = {"and","or","bitand","bitor","compl","not","not_eq","xor"};
    2675             : static const std::set<std::string> altop(&altopData[0], &altopData[8]);
    2676           0 : static void simplifyName(simplecpp::TokenList &expr)
    2677             : {
    2678           0 :     for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
    2679           0 :         if (tok->name) {
    2680           0 :             if (altop.find(tok->str()) != altop.end()) {
    2681             :                 bool alt;
    2682           0 :                 if (tok->str() == "not" || tok->str() == "compl") {
    2683           0 :                     alt = isAlternativeUnaryOp(tok,tok->str());
    2684             :                 } else {
    2685           0 :                     alt = isAlternativeBinaryOp(tok,tok->str());
    2686             :                 }
    2687           0 :                 if (alt)
    2688           0 :                     continue;
    2689             :             }
    2690           0 :             tok->setstr("0");
    2691             :         }
    2692             :     }
    2693           0 : }
    2694             : 
    2695             : /*
    2696             :  * Reads at least minlen and at most maxlen digits (inc. prefix) in base base
    2697             :  * from s starting at position pos and converts them to a
    2698             :  * unsigned long long value, updating pos to point to the first
    2699             :  * unused element of s.
    2700             :  * Returns ULLONG_MAX if the result is not representable and
    2701             :  * throws if the above requirements were not possible to satisfy.
    2702             :  */
    2703           0 : static unsigned long long stringToULLbounded(
    2704             :     const std::string& s,
    2705             :     std::size_t& pos,
    2706             :     int base = 0,
    2707             :     std::ptrdiff_t minlen = 1,
    2708             :     std::size_t maxlen = std::string::npos
    2709             : )
    2710             : {
    2711           0 :     const std::string sub = s.substr(pos, maxlen);
    2712           0 :     const char * const start = sub.c_str();
    2713             :     char* end;
    2714           0 :     const unsigned long long value = std::strtoull(start, &end, base);
    2715           0 :     pos += end - start;
    2716           0 :     if (end - start < minlen)
    2717           0 :         throw std::runtime_error("expected digit");
    2718           0 :     return value;
    2719             : }
    2720             : 
    2721             : /* Converts character literal (including prefix, but not ud-suffix)
    2722             :  * to long long value.
    2723             :  *
    2724             :  * Assumes ASCII-compatible single-byte encoded str for narrow literals
    2725             :  * and UTF-8 otherwise.
    2726             :  *
    2727             :  * For target assumes
    2728             :  * - execution character set encoding matching str
    2729             :  * - UTF-32 execution wide-character set encoding
    2730             :  * - requirements for __STDC_UTF_16__, __STDC_UTF_32__ and __STDC_ISO_10646__ satisfied
    2731             :  * - char16_t is 16bit wide
    2732             :  * - char32_t is 32bit wide
    2733             :  * - wchar_t is 32bit wide and unsigned
    2734             :  * - matching char signedness to host
    2735             :  * - matching sizeof(int) to host
    2736             :  *
    2737             :  * For host assumes
    2738             :  * - ASCII-compatible execution character set
    2739             :  *
    2740             :  * For host and target assumes
    2741             :  * - CHAR_BIT == 8
    2742             :  * - two's complement
    2743             :  *
    2744             :  * Implements multi-character narrow literals according to GCC's behavior,
    2745             :  * except multi code unit universal character names are not supported.
    2746             :  * Multi-character wide literals are not supported.
    2747             :  * Limited support of universal character names for non-UTF-8 execution character set encodings.
    2748             :  */
    2749           0 : long long simplecpp::characterLiteralToLL(const std::string& str)
    2750             : {
    2751             :     // default is wide/utf32
    2752           0 :     bool narrow = false;
    2753           0 :     bool utf8 = false;
    2754           0 :     bool utf16 = false;
    2755             : 
    2756             :     std::size_t pos;
    2757             : 
    2758           0 :     if (!str.empty() && str[0] == '\'') {
    2759           0 :         narrow = true;
    2760           0 :         pos = 1;
    2761           0 :     } else if (str.size() >= 2 && str[0] == 'u' && str[1] == '\'') {
    2762           0 :         utf16 = true;
    2763           0 :         pos = 2;
    2764           0 :     } else if (str.size() >= 3 && str[0] == 'u' && str[1] == '8' && str[2] == '\'') {
    2765           0 :         utf8 = true;
    2766           0 :         pos = 3;
    2767           0 :     } else if (str.size() >= 2 && (str[0] == 'L' || str[0] == 'U') && str[1] == '\'') {
    2768           0 :         pos = 2;
    2769             :     } else
    2770           0 :         throw std::runtime_error("expected a character literal");
    2771             : 
    2772           0 :     unsigned long long multivalue = 0;
    2773             : 
    2774           0 :     std::size_t nbytes = 0;
    2775             : 
    2776           0 :     while (pos + 1 < str.size()) {
    2777           0 :         if (str[pos] == '\'' || str[pos] == '\n')
    2778           0 :             throw std::runtime_error("raw single quotes and newlines not allowed in character literals");
    2779             : 
    2780           0 :         if (nbytes >= 1 && !narrow)
    2781           0 :             throw std::runtime_error("multiple characters only supported in narrow character literals");
    2782             : 
    2783             :         unsigned long long value;
    2784             : 
    2785           0 :         if (str[pos] == '\\') {
    2786           0 :             pos++;
    2787           0 :             const char escape = str[pos++];
    2788             : 
    2789           0 :             if (pos >= str.size())
    2790           0 :                 throw std::runtime_error("unexpected end of character literal");
    2791             : 
    2792           0 :             switch (escape) {
    2793             :             // obscure GCC extensions
    2794           0 :             case '%':
    2795             :             case '(':
    2796             :             case '[':
    2797             :             case '{':
    2798             :             // standard escape sequences
    2799             :             case '\'':
    2800             :             case '"':
    2801             :             case '?':
    2802             :             case '\\':
    2803           0 :                 value = static_cast<unsigned char>(escape);
    2804           0 :                 break;
    2805             : 
    2806           0 :             case 'a':
    2807           0 :                 value = static_cast<unsigned char>('\a');
    2808           0 :                 break;
    2809           0 :             case 'b':
    2810           0 :                 value = static_cast<unsigned char>('\b');
    2811           0 :                 break;
    2812           0 :             case 'f':
    2813           0 :                 value = static_cast<unsigned char>('\f');
    2814           0 :                 break;
    2815           0 :             case 'n':
    2816           0 :                 value = static_cast<unsigned char>('\n');
    2817           0 :                 break;
    2818           0 :             case 'r':
    2819           0 :                 value = static_cast<unsigned char>('\r');
    2820           0 :                 break;
    2821           0 :             case 't':
    2822           0 :                 value = static_cast<unsigned char>('\t');
    2823           0 :                 break;
    2824           0 :             case 'v':
    2825           0 :                 value = static_cast<unsigned char>('\v');
    2826           0 :                 break;
    2827             : 
    2828             :             // GCC extension for ESC character
    2829           0 :             case 'e':
    2830             :             case 'E':
    2831           0 :                 value = static_cast<unsigned char>('\x1b');
    2832           0 :                 break;
    2833             : 
    2834           0 :             case '0':
    2835             :             case '1':
    2836             :             case '2':
    2837             :             case '3':
    2838             :             case '4':
    2839             :             case '5':
    2840             :             case '6':
    2841             :             case '7':
    2842             :                 // octal escape sequences consist of 1 to 3 digits
    2843           0 :                 value = stringToULLbounded(str, --pos, 8, 1, 3);
    2844           0 :                 break;
    2845             : 
    2846           0 :             case 'x':
    2847             :                 // hexadecimal escape sequences consist of at least 1 digit
    2848           0 :                 value = stringToULLbounded(str, pos, 16);
    2849           0 :                 break;
    2850             : 
    2851           0 :             case 'u':
    2852             :             case 'U': {
    2853             :                 // universal character names have exactly 4 or 8 digits
    2854           0 :                 const std::size_t ndigits = (escape == 'u' ? 4 : 8);
    2855           0 :                 value = stringToULLbounded(str, pos, 16, ndigits, ndigits);
    2856             : 
    2857             :                 // UTF-8 encodes code points above 0x7f in multiple code units
    2858             :                 // code points above 0x10ffff are not allowed
    2859           0 :                 if (((narrow || utf8) && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff)
    2860           0 :                     throw std::runtime_error("code point too large");
    2861             : 
    2862           0 :                 if (value >= 0xd800 && value <= 0xdfff)
    2863           0 :                     throw std::runtime_error("surrogate code points not allowed in universal character names");
    2864             : 
    2865           0 :                 break;
    2866             :             }
    2867             : 
    2868           0 :             default:
    2869           0 :                 throw std::runtime_error("invalid escape sequence");
    2870             :             }
    2871             :         } else {
    2872           0 :             value = static_cast<unsigned char>(str[pos++]);
    2873             : 
    2874           0 :             if (!narrow && value >= 0x80) {
    2875             :                 // Assuming this is a UTF-8 encoded code point.
    2876             :                 // This decoder may not completely validate the input.
    2877             :                 // Noncharacters are neither rejected nor replaced.
    2878             : 
    2879             :                 int additional_bytes;
    2880           0 :                 if (value >= 0xf5)  // higher values would result in code points above 0x10ffff
    2881           0 :                     throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid");
    2882           0 :                 if (value >= 0xf0)
    2883           0 :                     additional_bytes = 3;
    2884           0 :                 else if (value >= 0xe0)
    2885           0 :                     additional_bytes = 2;
    2886           0 :                 else if (value >= 0xc2) // 0xc0 and 0xc1 are always overlong 2-bytes encodings
    2887           0 :                     additional_bytes = 1;
    2888             :                 else
    2889           0 :                     throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid");
    2890             : 
    2891           0 :                 value &= (1 << (6 - additional_bytes)) - 1;
    2892             : 
    2893           0 :                 while (additional_bytes--) {
    2894           0 :                     if (pos + 1 >= str.size())
    2895           0 :                         throw std::runtime_error("assumed UTF-8 encoded source, but character literal ends unexpectedly");
    2896             : 
    2897           0 :                     const unsigned char c = str[pos++];
    2898             : 
    2899           0 :                     if (((c >> 6) != 2)    // ensure c has form 0xb10xxxxxx
    2900           0 :                         || (!value && additional_bytes == 1 && c < 0xa0)    // overlong 3-bytes encoding
    2901           0 :                         || (!value && additional_bytes == 2 && c < 0x90))   // overlong 4-bytes encoding
    2902           0 :                         throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid");
    2903             : 
    2904           0 :                     value = (value << 6) | (c & ((1 << 7) - 1));
    2905             :                 }
    2906             : 
    2907           0 :                 if (value >= 0xd800 && value <= 0xdfff)
    2908           0 :                     throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid");
    2909             : 
    2910           0 :                 if ((utf8 && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff)
    2911           0 :                     throw std::runtime_error("code point too large");
    2912             :             }
    2913             :         }
    2914             : 
    2915           0 :         if (((narrow || utf8) && value > std::numeric_limits<unsigned char>::max()) || (utf16 && value >> 16) || value >> 32)
    2916           0 :             throw std::runtime_error("numeric escape sequence too large");
    2917             : 
    2918           0 :         multivalue <<= CHAR_BIT;
    2919           0 :         multivalue |= value;
    2920           0 :         nbytes++;
    2921             :     }
    2922             : 
    2923           0 :     if (pos + 1 != str.size() || str[pos] != '\'')
    2924           0 :         throw std::runtime_error("missing closing quote in character literal");
    2925             : 
    2926           0 :     if (!nbytes)
    2927           0 :         throw std::runtime_error("empty character literal");
    2928             : 
    2929             :     // ordinary narrow character literal's value is determined by (possibly signed) char
    2930           0 :     if (narrow && nbytes == 1)
    2931           0 :         return static_cast<char>(multivalue);
    2932             : 
    2933             :     // while multi-character literal's value is determined by (signed) int
    2934           0 :     if (narrow)
    2935           0 :         return static_cast<int>(multivalue);
    2936             : 
    2937             :     // All other cases are unsigned. Since long long is at least 64bit wide,
    2938             :     // while the literals at most 32bit wide, the conversion preserves all values.
    2939           0 :     return multivalue;
    2940             : }
    2941             : 
    2942           0 : static void simplifyNumbers(simplecpp::TokenList &expr)
    2943             : {
    2944           0 :     for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
    2945           0 :         if (tok->str().size() == 1U)
    2946           0 :             continue;
    2947           0 :         if (tok->str().compare(0,2,"0x") == 0)
    2948           0 :             tok->setstr(toString(stringToULL(tok->str())));
    2949           0 :         else if (!tok->number && tok->str().find('\'') != std::string::npos)
    2950           0 :             tok->setstr(toString(simplecpp::characterLiteralToLL(tok->str())));
    2951             :     }
    2952           0 : }
    2953             : 
    2954           0 : static void simplifyComments(simplecpp::TokenList &expr)
    2955             : {
    2956           0 :     for (simplecpp::Token *tok = expr.front(); tok;) {
    2957           0 :         simplecpp::Token * const d = tok;
    2958           0 :         tok = tok->next;
    2959           0 :         if (d->comment)
    2960           0 :             expr.deleteToken(d);
    2961             :     }
    2962           0 : }
    2963             : 
    2964           0 : static long long evaluate(simplecpp::TokenList &expr, const simplecpp::DUI &dui, const std::map<std::string, std::size_t> &sizeOfType)
    2965             : {
    2966           0 :     simplifyComments(expr);
    2967           0 :     simplifySizeof(expr, sizeOfType);
    2968           0 :     simplifyHasInclude(expr, dui);
    2969           0 :     simplifyName(expr);
    2970           0 :     simplifyNumbers(expr);
    2971           0 :     expr.constFold();
    2972             :     // TODO: handle invalid expressions
    2973           0 :     return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str()) : 0LL;
    2974             : }
    2975             : 
    2976           0 : static const simplecpp::Token *gotoNextLine(const simplecpp::Token *tok)
    2977             : {
    2978           0 :     const unsigned int line = tok->location.line;
    2979           0 :     const unsigned int file = tok->location.fileIndex;
    2980           0 :     while (tok && tok->location.line == line && tok->location.fileIndex == file)
    2981           0 :         tok = tok->next;
    2982           0 :     return tok;
    2983             : }
    2984             : 
    2985             : #ifdef SIMPLECPP_WINDOWS
    2986             : 
    2987             : class NonExistingFilesCache {
    2988             : public:
    2989             :     NonExistingFilesCache() {}
    2990             : 
    2991             :     bool contains(const std::string& path) {
    2992             :         MyLock<MyMutex> lock(m_mutex);
    2993             :         return (m_pathSet.find(path) != m_pathSet.end());
    2994             :     }
    2995             : 
    2996             :     void add(const std::string& path) {
    2997             :         MyLock<MyMutex> lock(m_mutex);
    2998             :         m_pathSet.insert(path);
    2999             :     }
    3000             : 
    3001             :     void clear() {
    3002             :         MyLock<MyMutex> lock(m_mutex);
    3003             :         m_pathSet.clear();
    3004             :     }
    3005             : 
    3006             : private:
    3007             :     std::set<std::string> m_pathSet;
    3008             :     MyMutex m_mutex;
    3009             : };
    3010             : 
    3011             : static NonExistingFilesCache nonExistingFilesCache;
    3012             : 
    3013             : #endif
    3014             : 
    3015           0 : static std::string openHeader(std::ifstream &f, const std::string &path)
    3016             : {
    3017           0 :     std::string simplePath = simplecpp::simplifyPath(path);
    3018             : #ifdef SIMPLECPP_WINDOWS
    3019             :     if (nonExistingFilesCache.contains(simplePath))
    3020             :         return "";  // file is known not to exist, skip expensive file open call
    3021             : #endif
    3022           0 :     f.open(simplePath.c_str());
    3023           0 :     if (f.is_open())
    3024           0 :         return simplePath;
    3025             : #ifdef SIMPLECPP_WINDOWS
    3026             :     nonExistingFilesCache.add(simplePath);
    3027             : #endif
    3028           0 :     return "";
    3029             : }
    3030             : 
    3031           0 : static std::string getRelativeFileName(const std::string &sourcefile, const std::string &header)
    3032             : {
    3033           0 :     if (sourcefile.find_first_of("\\/") != std::string::npos)
    3034           0 :         return simplecpp::simplifyPath(sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header);
    3035           0 :     return simplecpp::simplifyPath(header);
    3036             : }
    3037             : 
    3038           0 : static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header)
    3039             : {
    3040           0 :     return openHeader(f, getRelativeFileName(sourcefile, header));
    3041             : }
    3042             : 
    3043           0 : static std::string getIncludePathFileName(const std::string &includePath, const std::string &header)
    3044             : {
    3045           0 :     std::string path = includePath;
    3046           0 :     if (!path.empty() && path[path.size()-1U]!='/' && path[path.size()-1U]!='\\')
    3047           0 :         path += '/';
    3048           0 :     return path + header;
    3049             : }
    3050             : 
    3051           0 : static std::string openHeaderIncludePath(std::ifstream &f, const simplecpp::DUI &dui, const std::string &header)
    3052             : {
    3053           0 :     for (std::list<std::string>::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) {
    3054           0 :         std::string simplePath = openHeader(f, getIncludePathFileName(*it, header));
    3055           0 :         if (!simplePath.empty())
    3056           0 :             return simplePath;
    3057             :     }
    3058           0 :     return "";
    3059             : }
    3060             : 
    3061           0 : static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader)
    3062             : {
    3063           0 :     if (isAbsolutePath(header))
    3064           0 :         return openHeader(f, header);
    3065             : 
    3066           0 :     std::string ret;
    3067             : 
    3068           0 :     if (systemheader) {
    3069           0 :         ret = openHeaderIncludePath(f, dui, header);
    3070           0 :         return ret;
    3071             :     }
    3072             : 
    3073           0 :     ret = openHeaderRelative(f, sourcefile, header);
    3074           0 :     if (ret.empty())
    3075           0 :         return openHeaderIncludePath(f, dui, header);
    3076           0 :     return ret;
    3077             : }
    3078             : 
    3079           0 : static std::string getFileName(const std::map<std::string, simplecpp::TokenList *> &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader)
    3080             : {
    3081           0 :     if (filedata.empty()) {
    3082           0 :         return "";
    3083             :     }
    3084           0 :     if (isAbsolutePath(header)) {
    3085           0 :         return (filedata.find(header) != filedata.end()) ? simplecpp::simplifyPath(header) : "";
    3086             :     }
    3087             : 
    3088           0 :     if (!systemheader) {
    3089           0 :         const std::string relativeFilename = getRelativeFileName(sourcefile, header);
    3090           0 :         if (filedata.find(relativeFilename) != filedata.end())
    3091           0 :             return relativeFilename;
    3092             :     }
    3093             : 
    3094           0 :     for (std::list<std::string>::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) {
    3095           0 :         std::string s = simplecpp::simplifyPath(getIncludePathFileName(*it, header));
    3096           0 :         if (filedata.find(s) != filedata.end())
    3097           0 :             return s;
    3098             :     }
    3099             : 
    3100           0 :     if (systemheader && filedata.find(header) != filedata.end())
    3101           0 :         return header;
    3102             : 
    3103           0 :     return "";
    3104             : }
    3105             : 
    3106           0 : static bool hasFile(const std::map<std::string, simplecpp::TokenList *> &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader)
    3107             : {
    3108           0 :     return !getFileName(filedata, sourcefile, header, dui, systemheader).empty();
    3109             : }
    3110             : 
    3111           0 : std::map<std::string, simplecpp::TokenList*> simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector<std::string> &filenames, const simplecpp::DUI &dui, simplecpp::OutputList *outputList)
    3112             : {
    3113             : #ifdef SIMPLECPP_WINDOWS
    3114             :     if (dui.clearIncludeCache)
    3115             :         nonExistingFilesCache.clear();
    3116             : #endif
    3117             : 
    3118           0 :     std::map<std::string, simplecpp::TokenList*> ret;
    3119             : 
    3120           0 :     std::list<const Token *> filelist;
    3121             : 
    3122             :     // -include files
    3123           0 :     for (std::list<std::string>::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) {
    3124           0 :         const std::string &filename = realFilename(*it);
    3125             : 
    3126           0 :         if (ret.find(filename) != ret.end())
    3127           0 :             continue;
    3128             : 
    3129           0 :         std::ifstream fin(filename.c_str());
    3130           0 :         if (!fin.is_open()) {
    3131           0 :             if (outputList) {
    3132           0 :                 simplecpp::Output err(filenames);
    3133           0 :                 err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND;
    3134           0 :                 err.location = Location(filenames);
    3135           0 :                 err.msg = "Can not open include file '" + filename + "' that is explicitly included.";
    3136           0 :                 outputList->push_back(err);
    3137             :             }
    3138           0 :             continue;
    3139             :         }
    3140           0 :         fin.close();
    3141             : 
    3142           0 :         TokenList *tokenlist = new TokenList(filename, filenames, outputList);
    3143           0 :         if (!tokenlist->front()) {
    3144           0 :             delete tokenlist;
    3145           0 :             continue;
    3146             :         }
    3147             : 
    3148           0 :         if (dui.removeComments)
    3149           0 :             tokenlist->removeComments();
    3150           0 :         ret[filename] = tokenlist;
    3151           0 :         filelist.push_back(tokenlist->front());
    3152             :     }
    3153             : 
    3154           0 :     for (const Token *rawtok = rawtokens.cfront(); rawtok || !filelist.empty(); rawtok = rawtok ? rawtok->next : nullptr) {
    3155           0 :         if (rawtok == nullptr) {
    3156           0 :             rawtok = filelist.back();
    3157           0 :             filelist.pop_back();
    3158             :         }
    3159             : 
    3160           0 :         if (rawtok->op != '#' || sameline(rawtok->previousSkipComments(), rawtok))
    3161           0 :             continue;
    3162             : 
    3163           0 :         rawtok = rawtok->nextSkipComments();
    3164           0 :         if (!rawtok || rawtok->str() != INCLUDE)
    3165           0 :             continue;
    3166             : 
    3167           0 :         const std::string &sourcefile = rawtok->location.file();
    3168             : 
    3169           0 :         const Token * const htok = rawtok->nextSkipComments();
    3170           0 :         if (!sameline(rawtok, htok))
    3171           0 :             continue;
    3172             : 
    3173           0 :         const bool systemheader = (htok->str()[0] == '<');
    3174           0 :         const std::string header(realFilename(htok->str().substr(1U, htok->str().size() - 2U)));
    3175           0 :         if (hasFile(ret, sourcefile, header, dui, systemheader))
    3176           0 :             continue;
    3177             : 
    3178           0 :         std::ifstream f;
    3179           0 :         const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader);
    3180           0 :         if (!f.is_open())
    3181           0 :             continue;
    3182           0 :         f.close();
    3183             : 
    3184           0 :         TokenList *tokens = new TokenList(header2, filenames, outputList);
    3185           0 :         if (dui.removeComments)
    3186           0 :             tokens->removeComments();
    3187           0 :         ret[header2] = tokens;
    3188           0 :         if (tokens->front())
    3189           0 :             filelist.push_back(tokens->front());
    3190             :     }
    3191             : 
    3192           0 :     return ret;
    3193             : }
    3194             : 
    3195           0 : static bool preprocessToken(simplecpp::TokenList &output, const simplecpp::Token **tok1, simplecpp::MacroMap &macros, std::vector<std::string> &files, simplecpp::OutputList *outputList)
    3196             : {
    3197           0 :     const simplecpp::Token * const tok = *tok1;
    3198           0 :     const simplecpp::MacroMap::const_iterator it = macros.find(tok->str());
    3199           0 :     if (it != macros.end()) {
    3200           0 :         simplecpp::TokenList value(files);
    3201             :         try {
    3202           0 :             *tok1 = it->second.expand(&value, tok, macros, files);
    3203           0 :         } catch (simplecpp::Macro::Error &err) {
    3204           0 :             if (outputList) {
    3205           0 :                 simplecpp::Output out(files);
    3206           0 :                 out.type = simplecpp::Output::SYNTAX_ERROR;
    3207           0 :                 out.location = err.location;
    3208           0 :                 out.msg = "failed to expand \'" + tok->str() + "\', " + err.what;
    3209           0 :                 outputList->push_back(out);
    3210             :             }
    3211           0 :             return false;
    3212             :         }
    3213           0 :         output.takeTokens(value);
    3214             :     } else {
    3215           0 :         if (!tok->comment)
    3216           0 :             output.push_back(new simplecpp::Token(*tok));
    3217           0 :         *tok1 = tok->next;
    3218             :     }
    3219           0 :     return true;
    3220             : }
    3221             : 
    3222           0 : static void getLocaltime(struct tm &ltime)
    3223             : {
    3224             :     time_t t;
    3225           0 :     time(&t);
    3226             : #ifndef _WIN32
    3227             :     // NOLINTNEXTLINE(misc-include-cleaner) - false positive
    3228           0 :     localtime_r(&t, &ltime);
    3229             : #else
    3230             :     localtime_s(&ltime, &t);
    3231             : #endif
    3232           0 : }
    3233             : 
    3234           0 : static std::string getDateDefine(const struct tm *timep)
    3235             : {
    3236           0 :     char buf[] = "??? ?? ????";
    3237           0 :     strftime(buf, sizeof(buf), "%b %d %Y", timep);
    3238           0 :     return std::string("\"").append(buf).append("\"");
    3239             : }
    3240             : 
    3241           0 : static std::string getTimeDefine(const struct tm *timep)
    3242             : {
    3243           0 :     char buf[] = "??:??:??";
    3244           0 :     strftime(buf, sizeof(buf), "%T", timep);
    3245           0 :     return std::string("\"").append(buf).append("\"");
    3246             : }
    3247             : 
    3248           0 : void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector<std::string> &files, std::map<std::string, simplecpp::TokenList *> &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list<simplecpp::MacroUsage> *macroUsage, std::list<simplecpp::IfCond> *ifCond)
    3249             : {
    3250             : #ifdef SIMPLECPP_WINDOWS
    3251             :     if (dui.clearIncludeCache)
    3252             :         nonExistingFilesCache.clear();
    3253             : #endif
    3254             : 
    3255           0 :     std::map<std::string, std::size_t> sizeOfType(rawtokens.sizeOfType);
    3256           0 :     sizeOfType.insert(std::make_pair("char", sizeof(char)));
    3257           0 :     sizeOfType.insert(std::make_pair("short", sizeof(short)));
    3258           0 :     sizeOfType.insert(std::make_pair("short int", sizeOfType["short"]));
    3259           0 :     sizeOfType.insert(std::make_pair("int", sizeof(int)));
    3260           0 :     sizeOfType.insert(std::make_pair("long", sizeof(long)));
    3261           0 :     sizeOfType.insert(std::make_pair("long int", sizeOfType["long"]));
    3262           0 :     sizeOfType.insert(std::make_pair("long long", sizeof(long long)));
    3263           0 :     sizeOfType.insert(std::make_pair("float", sizeof(float)));
    3264           0 :     sizeOfType.insert(std::make_pair("double", sizeof(double)));
    3265           0 :     sizeOfType.insert(std::make_pair("long double", sizeof(long double)));
    3266           0 :     sizeOfType.insert(std::make_pair("char *", sizeof(char *)));
    3267           0 :     sizeOfType.insert(std::make_pair("short *", sizeof(short *)));
    3268           0 :     sizeOfType.insert(std::make_pair("short int *", sizeOfType["short *"]));
    3269           0 :     sizeOfType.insert(std::make_pair("int *", sizeof(int *)));
    3270           0 :     sizeOfType.insert(std::make_pair("long *", sizeof(long *)));
    3271           0 :     sizeOfType.insert(std::make_pair("long int *", sizeOfType["long *"]));
    3272           0 :     sizeOfType.insert(std::make_pair("long long *", sizeof(long long *)));
    3273           0 :     sizeOfType.insert(std::make_pair("float *", sizeof(float *)));
    3274           0 :     sizeOfType.insert(std::make_pair("double *", sizeof(double *)));
    3275           0 :     sizeOfType.insert(std::make_pair("long double *", sizeof(long double *)));
    3276             : 
    3277             :     // use a dummy vector for the macros because as this is not part of the file and would add an empty entry - e.g. /usr/include/poll.h
    3278           0 :     std::vector<std::string> dummy;
    3279             : 
    3280           0 :     const bool hasInclude = (dui.std.size() == 5 && dui.std.compare(0,3,"c++") == 0 && dui.std >= "c++17");
    3281           0 :     MacroMap macros;
    3282           0 :     for (std::list<std::string>::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) {
    3283           0 :         const std::string &macrostr = *it;
    3284           0 :         const std::string::size_type eq = macrostr.find('=');
    3285           0 :         const std::string::size_type par = macrostr.find('(');
    3286           0 :         const std::string macroname = macrostr.substr(0, std::min(eq,par));
    3287           0 :         if (dui.undefined.find(macroname) != dui.undefined.end())
    3288           0 :             continue;
    3289           0 :         const std::string lhs(macrostr.substr(0,eq));
    3290           0 :         const std::string rhs(eq==std::string::npos ? std::string("1") : macrostr.substr(eq+1));
    3291           0 :         const Macro macro(lhs, rhs, dummy);
    3292           0 :         macros.insert(std::pair<TokenString,Macro>(macro.name(), macro));
    3293             :     }
    3294             : 
    3295           0 :     macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", dummy)));
    3296           0 :     macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", dummy)));
    3297           0 :     macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", dummy)));
    3298           0 :     struct tm ltime = {};
    3299           0 :     getLocaltime(ltime);
    3300           0 :     macros.insert(std::make_pair("__DATE__", Macro("__DATE__", getDateDefine(&ltime), dummy)));
    3301           0 :     macros.insert(std::make_pair("__TIME__", Macro("__TIME__", getTimeDefine(&ltime), dummy)));
    3302             : 
    3303           0 :     if (!dui.std.empty()) {
    3304           0 :         std::string std_def = simplecpp::getCStdString(dui.std);
    3305           0 :         if (!std_def.empty()) {
    3306           0 :             macros.insert(std::make_pair("__STDC_VERSION__", Macro("__STDC_VERSION__", std_def, dummy)));
    3307             :         } else {
    3308           0 :             std_def = simplecpp::getCppStdString(dui.std);
    3309           0 :             if (!std_def.empty())
    3310           0 :                 macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", std_def, dummy)));
    3311             :         }
    3312             :     }
    3313             : 
    3314             :     // True => code in current #if block should be kept
    3315             :     // ElseIsTrue => code in current #if block should be dropped. the code in the #else should be kept.
    3316             :     // AlwaysFalse => drop all code in #if and #else
    3317             :     enum IfState { True, ElseIsTrue, AlwaysFalse };
    3318           0 :     std::stack<int> ifstates;
    3319           0 :     ifstates.push(True);
    3320             : 
    3321           0 :     std::stack<const Token *> includetokenstack;
    3322             : 
    3323           0 :     std::set<std::string> pragmaOnce;
    3324             : 
    3325           0 :     includetokenstack.push(rawtokens.cfront());
    3326           0 :     for (std::list<std::string>::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) {
    3327           0 :         const std::map<std::string, TokenList*>::const_iterator f = filedata.find(*it);
    3328           0 :         if (f != filedata.end())
    3329           0 :             includetokenstack.push(f->second->cfront());
    3330             :     }
    3331             : 
    3332           0 :     std::map<std::string, std::list<Location> > maybeUsedMacros;
    3333             : 
    3334           0 :     for (const Token *rawtok = nullptr; rawtok || !includetokenstack.empty();) {
    3335           0 :         if (rawtok == nullptr) {
    3336           0 :             rawtok = includetokenstack.top();
    3337           0 :             includetokenstack.pop();
    3338           0 :             continue;
    3339             :         }
    3340             : 
    3341           0 :         if (rawtok->op == '#' && !sameline(rawtok->previousSkipComments(), rawtok)) {
    3342           0 :             if (!sameline(rawtok, rawtok->next)) {
    3343           0 :                 rawtok = rawtok->next;
    3344           0 :                 continue;
    3345             :             }
    3346           0 :             rawtok = rawtok->next;
    3347           0 :             if (!rawtok->name) {
    3348           0 :                 rawtok = gotoNextLine(rawtok);
    3349           0 :                 continue;
    3350             :             }
    3351             : 
    3352           0 :             if (ifstates.size() <= 1U && (rawtok->str() == ELIF || rawtok->str() == ELSE || rawtok->str() == ENDIF)) {
    3353           0 :                 if (outputList) {
    3354           0 :                     simplecpp::Output err(files);
    3355           0 :                     err.type = Output::SYNTAX_ERROR;
    3356           0 :                     err.location = rawtok->location;
    3357           0 :                     err.msg = "#" + rawtok->str() + " without #if";
    3358           0 :                     outputList->push_back(err);
    3359             :                 }
    3360           0 :                 output.clear();
    3361           0 :                 return;
    3362             :             }
    3363             : 
    3364           0 :             if (ifstates.top() == True && (rawtok->str() == ERROR || rawtok->str() == WARNING)) {
    3365           0 :                 if (outputList) {
    3366           0 :                     simplecpp::Output err(rawtok->location.files);
    3367           0 :                     err.type = rawtok->str() == ERROR ? Output::ERROR : Output::WARNING;
    3368           0 :                     err.location = rawtok->location;
    3369           0 :                     for (const Token *tok = rawtok->next; tok && sameline(rawtok,tok); tok = tok->next) {
    3370           0 :                         if (!err.msg.empty() && isNameChar(tok->str()[0]))
    3371           0 :                             err.msg += ' ';
    3372           0 :                         err.msg += tok->str();
    3373             :                     }
    3374           0 :                     err.msg = '#' + rawtok->str() + ' ' + err.msg;
    3375           0 :                     outputList->push_back(err);
    3376             :                 }
    3377           0 :                 if (rawtok->str() == ERROR) {
    3378           0 :                     output.clear();
    3379           0 :                     return;
    3380             :                 }
    3381             :             }
    3382             : 
    3383           0 :             if (rawtok->str() == DEFINE) {
    3384           0 :                 if (ifstates.top() != True)
    3385           0 :                     continue;
    3386             :                 try {
    3387           0 :                     const Macro &macro = Macro(rawtok->previous, files);
    3388           0 :                     if (dui.undefined.find(macro.name()) == dui.undefined.end()) {
    3389           0 :                         const MacroMap::iterator it = macros.find(macro.name());
    3390           0 :                         if (it == macros.end())
    3391           0 :                             macros.insert(std::pair<TokenString, Macro>(macro.name(), macro));
    3392             :                         else
    3393           0 :                             it->second = macro;
    3394             :                     }
    3395           0 :                 } catch (const std::runtime_error &) {
    3396           0 :                     if (outputList) {
    3397           0 :                         simplecpp::Output err(files);
    3398           0 :                         err.type = Output::SYNTAX_ERROR;
    3399           0 :                         err.location = rawtok->location;
    3400           0 :                         err.msg = "Failed to parse #define";
    3401           0 :                         outputList->push_back(err);
    3402             :                     }
    3403           0 :                     output.clear();
    3404           0 :                     return;
    3405             :                 }
    3406           0 :             } else if (ifstates.top() == True && rawtok->str() == INCLUDE) {
    3407           0 :                 TokenList inc1(files);
    3408           0 :                 for (const Token *inctok = rawtok->next; sameline(rawtok,inctok); inctok = inctok->next) {
    3409           0 :                     if (!inctok->comment)
    3410           0 :                         inc1.push_back(new Token(*inctok));
    3411             :                 }
    3412           0 :                 TokenList inc2(files);
    3413           0 :                 if (!inc1.empty() && inc1.cfront()->name) {
    3414           0 :                     const Token *inctok = inc1.cfront();
    3415           0 :                     if (!preprocessToken(inc2, &inctok, macros, files, outputList)) {
    3416           0 :                         output.clear();
    3417           0 :                         return;
    3418             :                     }
    3419             :                 } else {
    3420           0 :                     inc2.takeTokens(inc1);
    3421             :                 }
    3422             : 
    3423           0 :                 if (!inc2.empty() && inc2.cfront()->op == '<' && inc2.cback()->op == '>') {
    3424           0 :                     TokenString hdr;
    3425             :                     // TODO: Sometimes spaces must be added in the string
    3426             :                     // Somehow preprocessToken etc must be told that the location should be source location not destination location
    3427           0 :                     for (const Token *tok = inc2.cfront(); tok; tok = tok->next) {
    3428           0 :                         hdr += tok->str();
    3429             :                     }
    3430           0 :                     inc2.clear();
    3431           0 :                     inc2.push_back(new Token(hdr, inc1.cfront()->location));
    3432           0 :                     inc2.front()->op = '<';
    3433             :                 }
    3434             : 
    3435           0 :                 if (inc2.empty() || inc2.cfront()->str().size() <= 2U) {
    3436           0 :                     if (outputList) {
    3437           0 :                         simplecpp::Output err(files);
    3438           0 :                         err.type = Output::SYNTAX_ERROR;
    3439           0 :                         err.location = rawtok->location;
    3440           0 :                         err.msg = "No header in #include";
    3441           0 :                         outputList->push_back(err);
    3442             :                     }
    3443           0 :                     output.clear();
    3444           0 :                     return;
    3445             :                 }
    3446             : 
    3447           0 :                 const Token * const inctok = inc2.cfront();
    3448             : 
    3449           0 :                 const bool systemheader = (inctok->str()[0] == '<');
    3450           0 :                 const std::string header(realFilename(inctok->str().substr(1U, inctok->str().size() - 2U)));
    3451           0 :                 std::string header2 = getFileName(filedata, rawtok->location.file(), header, dui, systemheader);
    3452           0 :                 if (header2.empty()) {
    3453             :                     // try to load file..
    3454           0 :                     std::ifstream f;
    3455           0 :                     header2 = openHeader(f, dui, rawtok->location.file(), header, systemheader);
    3456           0 :                     if (f.is_open()) {
    3457           0 :                         TokenList * const tokens = new TokenList(f, files, header2, outputList);
    3458           0 :                         if (dui.removeComments)
    3459           0 :                             tokens->removeComments();
    3460           0 :                         filedata[header2] = tokens;
    3461             :                     }
    3462             :                 }
    3463           0 :                 if (header2.empty()) {
    3464           0 :                     if (outputList) {
    3465           0 :                         simplecpp::Output out(files);
    3466           0 :                         out.type = Output::MISSING_HEADER;
    3467           0 :                         out.location = rawtok->location;
    3468           0 :                         out.msg = "Header not found: " + inctok->str();
    3469           0 :                         outputList->push_back(out);
    3470             :                     }
    3471           0 :                 } else if (includetokenstack.size() >= 400) {
    3472           0 :                     if (outputList) {
    3473           0 :                         simplecpp::Output out(files);
    3474           0 :                         out.type = Output::INCLUDE_NESTED_TOO_DEEPLY;
    3475           0 :                         out.location = rawtok->location;
    3476           0 :                         out.msg = "#include nested too deeply";
    3477           0 :                         outputList->push_back(out);
    3478             :                     }
    3479           0 :                 } else if (pragmaOnce.find(header2) == pragmaOnce.end()) {
    3480           0 :                     includetokenstack.push(gotoNextLine(rawtok));
    3481           0 :                     const TokenList * const includetokens = filedata.find(header2)->second;
    3482           0 :                     rawtok = includetokens ? includetokens->cfront() : nullptr;
    3483           0 :                     continue;
    3484             :                 }
    3485           0 :             } else if (rawtok->str() == IF || rawtok->str() == IFDEF || rawtok->str() == IFNDEF || rawtok->str() == ELIF) {
    3486           0 :                 if (!sameline(rawtok,rawtok->next)) {
    3487           0 :                     if (outputList) {
    3488           0 :                         simplecpp::Output out(files);
    3489           0 :                         out.type = Output::SYNTAX_ERROR;
    3490           0 :                         out.location = rawtok->location;
    3491           0 :                         out.msg = "Syntax error in #" + rawtok->str();
    3492           0 :                         outputList->push_back(out);
    3493             :                     }
    3494           0 :                     output.clear();
    3495           0 :                     return;
    3496             :                 }
    3497             : 
    3498             :                 bool conditionIsTrue;
    3499           0 :                 if (ifstates.top() == AlwaysFalse || (ifstates.top() == ElseIsTrue && rawtok->str() != ELIF))
    3500           0 :                     conditionIsTrue = false;
    3501           0 :                 else if (rawtok->str() == IFDEF) {
    3502           0 :                     conditionIsTrue = (macros.find(rawtok->next->str()) != macros.end() || (hasInclude && rawtok->next->str() == HAS_INCLUDE));
    3503           0 :                     maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location);
    3504           0 :                 } else if (rawtok->str() == IFNDEF) {
    3505           0 :                     conditionIsTrue = (macros.find(rawtok->next->str()) == macros.end() && !(hasInclude && rawtok->next->str() == HAS_INCLUDE));
    3506           0 :                     maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location);
    3507             :                 } else { /*if (rawtok->str() == IF || rawtok->str() == ELIF)*/
    3508           0 :                     TokenList expr(files);
    3509           0 :                     for (const Token *tok = rawtok->next; tok && tok->location.sameline(rawtok->location); tok = tok->next) {
    3510           0 :                         if (!tok->name) {
    3511           0 :                             expr.push_back(new Token(*tok));
    3512           0 :                             continue;
    3513             :                         }
    3514             : 
    3515           0 :                         if (tok->str() == DEFINED) {
    3516           0 :                             tok = tok->next;
    3517           0 :                             const bool par = (tok && tok->op == '(');
    3518           0 :                             if (par)
    3519           0 :                                 tok = tok->next;
    3520           0 :                             maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location);
    3521           0 :                             if (tok) {
    3522           0 :                                 if (macros.find(tok->str()) != macros.end())
    3523           0 :                                     expr.push_back(new Token("1", tok->location));
    3524           0 :                                 else if (hasInclude && tok->str() == HAS_INCLUDE)
    3525           0 :                                     expr.push_back(new Token("1", tok->location));
    3526             :                                 else
    3527           0 :                                     expr.push_back(new Token("0", tok->location));
    3528             :                             }
    3529           0 :                             if (par)
    3530           0 :                                 tok = tok ? tok->next : nullptr;
    3531           0 :                             if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')')) {
    3532           0 :                                 if (outputList) {
    3533           0 :                                     Output out(rawtok->location.files);
    3534           0 :                                     out.type = Output::SYNTAX_ERROR;
    3535           0 :                                     out.location = rawtok->location;
    3536           0 :                                     out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition";
    3537           0 :                                     outputList->push_back(out);
    3538             :                                 }
    3539           0 :                                 output.clear();
    3540           0 :                                 return;
    3541             :                             }
    3542           0 :                             continue;
    3543             :                         }
    3544             : 
    3545           0 :                         if (hasInclude && tok->str() == HAS_INCLUDE) {
    3546           0 :                             tok = tok->next;
    3547           0 :                             const bool par = (tok && tok->op == '(');
    3548           0 :                             if (par)
    3549           0 :                                 tok = tok->next;
    3550           0 :                             bool closingAngularBracket = false;
    3551           0 :                             if (tok) {
    3552           0 :                                 const std::string &sourcefile = rawtok->location.file();
    3553           0 :                                 const bool systemheader = (tok && tok->op == '<');
    3554           0 :                                 std::string header;
    3555             : 
    3556           0 :                                 if (systemheader) {
    3557           0 :                                     while ((tok = tok->next) && tok->op != '>')
    3558           0 :                                         header += tok->str();
    3559             :                                     // cppcheck-suppress selfAssignment - platform-dependent implementation
    3560           0 :                                     header = realFilename(header);
    3561           0 :                                     if (tok && tok->op == '>')
    3562           0 :                                         closingAngularBracket = true;
    3563             :                                 }
    3564             :                                 else {
    3565           0 :                                     header = realFilename(tok->str().substr(1U, tok->str().size() - 2U));
    3566           0 :                                     closingAngularBracket = true;
    3567             :                                 }
    3568           0 :                                 std::ifstream f;
    3569           0 :                                 const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader);
    3570           0 :                                 expr.push_back(new Token(header2.empty() ? "0" : "1", tok->location));
    3571             :                             }
    3572           0 :                             if (par)
    3573           0 :                                 tok = tok ? tok->next : nullptr;
    3574           0 :                             if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')') || (!closingAngularBracket)) {
    3575           0 :                                 if (outputList) {
    3576           0 :                                     Output out(rawtok->location.files);
    3577           0 :                                     out.type = Output::SYNTAX_ERROR;
    3578           0 :                                     out.location = rawtok->location;
    3579           0 :                                     out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition";
    3580           0 :                                     outputList->push_back(out);
    3581             :                                 }
    3582           0 :                                 output.clear();
    3583           0 :                                 return;
    3584             :                             }
    3585           0 :                             continue;
    3586             :                         }
    3587             : 
    3588           0 :                         maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location);
    3589             : 
    3590           0 :                         const Token *tmp = tok;
    3591           0 :                         if (!preprocessToken(expr, &tmp, macros, files, outputList)) {
    3592           0 :                             output.clear();
    3593           0 :                             return;
    3594             :                         }
    3595           0 :                         if (!tmp)
    3596           0 :                             break;
    3597           0 :                         tok = tmp->previous;
    3598             :                     }
    3599             :                     try {
    3600           0 :                         if (ifCond) {
    3601           0 :                             std::string E;
    3602           0 :                             for (const simplecpp::Token *tok = expr.cfront(); tok; tok = tok->next)
    3603           0 :                                 E += (E.empty() ? "" : " ") + tok->str();
    3604           0 :                             const long long result = evaluate(expr, dui, sizeOfType);
    3605           0 :                             conditionIsTrue = (result != 0);
    3606           0 :                             ifCond->push_back(IfCond(rawtok->location, E, result));
    3607             :                         } else {
    3608           0 :                             const long long result = evaluate(expr, dui, sizeOfType);
    3609           0 :                             conditionIsTrue = (result != 0);
    3610             :                         }
    3611           0 :                     } catch (const std::exception &e) {
    3612           0 :                         if (outputList) {
    3613           0 :                             Output out(rawtok->location.files);
    3614           0 :                             out.type = Output::SYNTAX_ERROR;
    3615           0 :                             out.location = rawtok->location;
    3616           0 :                             out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition";
    3617           0 :                             if (e.what() && *e.what())
    3618           0 :                                 out.msg += std::string(", ") + e.what();
    3619           0 :                             outputList->push_back(out);
    3620             :                         }
    3621           0 :                         output.clear();
    3622           0 :                         return;
    3623             :                     }
    3624             :                 }
    3625             : 
    3626           0 :                 if (rawtok->str() != ELIF) {
    3627             :                     // push a new ifstate..
    3628           0 :                     if (ifstates.top() != True)
    3629           0 :                         ifstates.push(AlwaysFalse);
    3630             :                     else
    3631           0 :                         ifstates.push(conditionIsTrue ? True : ElseIsTrue);
    3632           0 :                 } else if (ifstates.top() == True) {
    3633           0 :                     ifstates.top() = AlwaysFalse;
    3634           0 :                 } else if (ifstates.top() == ElseIsTrue && conditionIsTrue) {
    3635           0 :                     ifstates.top() = True;
    3636             :                 }
    3637           0 :             } else if (rawtok->str() == ELSE) {
    3638           0 :                 ifstates.top() = (ifstates.top() == ElseIsTrue) ? True : AlwaysFalse;
    3639           0 :             } else if (rawtok->str() == ENDIF) {
    3640           0 :                 ifstates.pop();
    3641           0 :             } else if (rawtok->str() == UNDEF) {
    3642           0 :                 if (ifstates.top() == True) {
    3643           0 :                     const Token *tok = rawtok->next;
    3644           0 :                     while (sameline(rawtok,tok) && tok->comment)
    3645           0 :                         tok = tok->next;
    3646           0 :                     if (sameline(rawtok, tok))
    3647           0 :                         macros.erase(tok->str());
    3648             :                 }
    3649           0 :             } else if (ifstates.top() == True && rawtok->str() == PRAGMA && rawtok->next && rawtok->next->str() == ONCE && sameline(rawtok,rawtok->next)) {
    3650           0 :                 pragmaOnce.insert(rawtok->location.file());
    3651             :             }
    3652           0 :             rawtok = gotoNextLine(rawtok);
    3653           0 :             continue;
    3654             :         }
    3655             : 
    3656           0 :         if (ifstates.top() != True) {
    3657             :             // drop code
    3658           0 :             rawtok = gotoNextLine(rawtok);
    3659           0 :             continue;
    3660             :         }
    3661             : 
    3662           0 :         bool hash=false, hashhash=false;
    3663           0 :         if (rawtok->op == '#' && sameline(rawtok,rawtok->next)) {
    3664           0 :             if (rawtok->next->op != '#') {
    3665           0 :                 hash = true;
    3666           0 :                 rawtok = rawtok->next; // skip '#'
    3667           0 :             } else if (sameline(rawtok,rawtok->next->next)) {
    3668           0 :                 hashhash = true;
    3669           0 :                 rawtok = rawtok->next->next; // skip '#' '#'
    3670             :             }
    3671             :         }
    3672             : 
    3673           0 :         const Location loc(rawtok->location);
    3674           0 :         TokenList tokens(files);
    3675             : 
    3676           0 :         if (!preprocessToken(tokens, &rawtok, macros, files, outputList)) {
    3677           0 :             output.clear();
    3678           0 :             return;
    3679             :         }
    3680             : 
    3681           0 :         if (hash || hashhash) {
    3682           0 :             std::string s;
    3683           0 :             for (const Token *hashtok = tokens.cfront(); hashtok; hashtok = hashtok->next)
    3684           0 :                 s += hashtok->str();
    3685           0 :             if (hash)
    3686           0 :                 output.push_back(new Token('\"' + s + '\"', loc));
    3687           0 :             else if (output.back())
    3688           0 :                 output.back()->setstr(output.cback()->str() + s);
    3689             :             else
    3690           0 :                 output.push_back(new Token(s, loc));
    3691             :         } else {
    3692           0 :             output.takeTokens(tokens);
    3693             :         }
    3694             :     }
    3695             : 
    3696           0 :     if (macroUsage) {
    3697           0 :         for (simplecpp::MacroMap::const_iterator macroIt = macros.begin(); macroIt != macros.end(); ++macroIt) {
    3698           0 :             const Macro &macro = macroIt->second;
    3699           0 :             std::list<Location> usage = macro.usage();
    3700           0 :             const std::list<Location>& temp = maybeUsedMacros[macro.name()];
    3701           0 :             usage.insert(usage.end(), temp.begin(), temp.end());
    3702           0 :             for (std::list<Location>::const_iterator usageIt = usage.begin(); usageIt != usage.end(); ++usageIt) {
    3703           0 :                 MacroUsage mu(usageIt->files, macro.valueDefinedInCode());
    3704           0 :                 mu.macroName = macro.name();
    3705           0 :                 mu.macroLocation = macro.defineLocation();
    3706           0 :                 mu.useLocation = *usageIt;
    3707           0 :                 macroUsage->push_back(mu);
    3708             :             }
    3709             :         }
    3710             :     }
    3711             : }
    3712             : 
    3713           0 : void simplecpp::cleanup(std::map<std::string, TokenList*> &filedata)
    3714             : {
    3715           0 :     for (std::map<std::string, TokenList*>::iterator it = filedata.begin(); it != filedata.end(); ++it)
    3716           0 :         delete it->second;
    3717           0 :     filedata.clear();
    3718           0 : }
    3719             : 
    3720           0 : std::string simplecpp::getCStdString(const std::string &std)
    3721             : {
    3722           0 :     if (std == "c90" || std == "c89" || std == "iso9899:1990" || std == "iso9899:199409" || std == "gnu90" || std == "gnu89") {
    3723             :         // __STDC_VERSION__ is not set for C90 although the macro was added in the 1994 amendments
    3724           0 :         return "";
    3725             :     }
    3726           0 :     if (std == "c99" || std == "c9x" || std == "iso9899:1999" || std == "iso9899:199x" || std == "gnu99"|| std == "gnu9x")
    3727           0 :         return "199901L";
    3728           0 :     if (std == "c11" || std == "c1x" || std == "iso9899:2011" || std == "gnu11" || std == "gnu1x")
    3729           0 :         return "201112L";
    3730           0 :     if (std == "c17" || std == "c18" || std == "iso9899:2017" || std == "iso9899:2018" || std == "gnu17"|| std == "gnu18")
    3731           0 :         return "201710L";
    3732           0 :     if (std == "c23" || std == "gnu23" || std == "c2x" || std == "gnu2x") {
    3733             :         // supported by GCC 9+ and Clang 9+
    3734             :         // Clang 9, 10, 11, 12, 13 return "201710L"
    3735             :         // Clang 14, 15, 16, 17 return "202000L"
    3736             :         // Clang 9, 10, 11, 12, 13, 14, 15, 16, 17 do not support "c23" and "gnu23"
    3737           0 :         return "202311L";
    3738             :     }
    3739           0 :     return "";
    3740             : }
    3741             : 
    3742           0 : std::string simplecpp::getCppStdString(const std::string &std)
    3743             : {
    3744           0 :     if (std == "c++98" || std == "c++03" || std == "gnu++98" || std == "gnu++03")
    3745           0 :         return "199711L";
    3746           0 :     if (std == "c++11" || std == "gnu++11" || std == "c++0x" || std == "gnu++0x")
    3747           0 :         return "201103L";
    3748           0 :     if (std == "c++14" || std == "c++1y" || std == "gnu++14" || std == "gnu++1y")
    3749           0 :         return "201402L";
    3750           0 :     if (std == "c++17" || std == "c++1z" || std == "gnu++17" || std == "gnu++1z")
    3751           0 :         return "201703L";
    3752           0 :     if (std == "c++20" || std == "c++2a" || std == "gnu++20" || std == "gnu++2a") {
    3753             :         // GCC 10 returns "201703L" - correct in 11+
    3754           0 :         return "202002L";
    3755             :     }
    3756           0 :     if (std == "c++23" || std == "c++2b" || std == "gnu++23" || std == "gnu++2b") {
    3757             :         // supported by GCC 11+ and Clang 12+
    3758             :         // GCC 11, 12, 13 return "202100L"
    3759             :         // Clang 12, 13, 14, 15, 16 do not support "c++23" and "gnu++23" and return "202101L"
    3760             :         // Clang 17, 18 return "202302L"
    3761           0 :         return "202302L";
    3762             :     }
    3763           0 :     if (std == "c++26" || std == "c++2c" || std == "gnu++26" || std == "gnu++2c") {
    3764             :         // supported by Clang 17+
    3765           0 :         return "202400L";
    3766             :     }
    3767           0 :     return "";
    3768             : }
    3769             : 
    3770             : #if (__cplusplus < 201103L) && !defined(__APPLE__)
    3771             : #undef nullptr
    3772             : #endif

Generated by: LCOV version 1.14