[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/tmpvar/simplecpp/master/simplecpp.cpp [Back]  [Original]

/*
 * simplecpp - A simple and high-fidelity C/C++ preprocessor library
 * Copyright (C) 2016-2023 simplecpp team
 */

#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
#define SIMPLECPP_WINDOWS
#define NOMINMAX
#endif

#include "simplecpp.h"

#include 
#include 
#include 
#include 
#include  // IWYU pragma: keep
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#if __cplusplus >= 201103L
#ifdef SIMPLECPP_WINDOWS
#include 
#endif
#include 
#endif
#include 
#include 

#ifdef SIMPLECPP_WINDOWS
#include 
#undef ERROR
#endif

#if __cplusplus >= 201103L
#define OVERRIDE override
#define EXPLICIT explicit
#else
#define OVERRIDE
#define EXPLICIT
#endif

#if (__cplusplus < 201103L) && !defined(__APPLE__)
#define nullptr NULL
#endif

static bool isHex(const std::string &s)
{
    return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0);
}

static bool isOct(const std::string &s)
{
    return s.size()>1 && (s[0]=='0') && (s[1] >= '0') && (s[1] < '8');
}

// 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
static bool isStringLiteral_(const std::string &s)
{
    return s.size() > 1 && (s[0]=='\"') && (*s.rbegin()=='\"');
}

// 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
static bool isCharLiteral_(const std::string &s)
{
    // char literal patterns can include 'a', '\t', '\000', '\xff', 'abcd', and maybe ''
    // This only checks for the surrounding '' but doesn't parse the content.
    return s.size() > 1 && (s[0]=='\'') && (*s.rbegin()=='\'');
}

static const simplecpp::TokenString DEFINE("define");
static const simplecpp::TokenString UNDEF("undef");

static const simplecpp::TokenString INCLUDE("include");

static const simplecpp::TokenString ERROR("error");
static const simplecpp::TokenString WARNING("warning");

static const simplecpp::TokenString IF("if");
static const simplecpp::TokenString IFDEF("ifdef");
static const simplecpp::TokenString IFNDEF("ifndef");
static const simplecpp::TokenString DEFINED("defined");
static const simplecpp::TokenString ELSE("else");
static const simplecpp::TokenString ELIF("elif");
static const simplecpp::TokenString ENDIF("endif");

static const simplecpp::TokenString PRAGMA("pragma");
static const simplecpp::TokenString ONCE("once");

static const simplecpp::TokenString HAS_INCLUDE("__has_include");

template static std::string toString(T t)
{
    // NOLINTNEXTLINE(misc-const-correctness) - false positive
    std::ostringstream ostr;
    ostr > std::hex;
    else if (oct)
        istr >> std::oct;
    istr >> ret;
    return ret;
}

static unsigned long long stringToULL(const std::string &s)
{
    unsigned long long ret;
    const bool hex = isHex(s);
    const bool oct = isOct(s);
    std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s);
    if (hex)
        istr >> std::hex;
    else if (oct)
        istr >> std::oct;
    istr >> ret;
    return ret;
}

static bool endsWith(const std::string &s, const std::string &e)
{
    return (s.size() >= e.size()) && std::equal(e.rbegin(), e.rend(), s.rbegin());
}

static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
{
    return tok1 && tok2 && tok1->location.sameline(tok2->location);
}

static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt)
{
    return (tok->name &&
            tok->str() == alt &&
            tok->previous &&
            tok->next &&
            (tok->previous->number || tok->previous->name || tok->previous->op == ')') &&
            (tok->next->number || tok->next->name || tok->next->op == '('));
}

static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt)
{
    return ((tok->name && tok->str() == alt) &&
            (!tok->previous || tok->previous->op == '(') &&
            (tok->next && (tok->next->name || tok->next->number)));
}

static std::string replaceAll(std::string s, const std::string& from, const std::string& to)
{
    for (size_t pos = s.find(from); pos != std::string::npos; pos = s.find(from, pos + to.size()))
        s.replace(pos, from.size(), to);
    return s;
}

const std::string simplecpp::Location::emptyFileName;

void simplecpp::Location::adjust(const std::string &str)
{
    if (strpbrk(str.c_str(), "\r\n") == nullptr) {
        col += str.size();
        return;
    }

    for (std::size_t i = 0U; i < str.size(); ++i) {
        col++;
        if (str[i] == '\n' || str[i] == '\r') {
            col = 1;
            line++;
            if (str[i] == '\r' && (i+1)previous)
        tok = tok->previous;
    for (; tok; tok = tok->next) {
        if (tok->previous) {
            std::cout previous) ? ' ' : '\n');
        }
        std::cout str();
    }
    std::cout next) {
        if (tok != this) {
            std::cout previous) ? ' ' : '\n');
        }
        std::cout str();
    }
    std::cout = 0x80) ? 0xff : ch16));
        }

        // Handling of newlines..
        if (ch == '\r') {
            ch = '\n';

            int ch2 = get();
            if (isUtf16) {
                const int c2 = get();
                ch2 = makeUtf16Char(ch2, c2);
            }

            if (ch2 != '\n')
                ungetChar();
        }

        return ch;
    }

    unsigned char peekChar()
    {
        unsigned char ch = static_cast(peek());

        // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the
        // character is non-ASCII character then replace it with 0xff
        if (isUtf16) {
            (void)get();
            const unsigned char ch2 = static_cast(peek());
            unget();
            const int ch16 = makeUtf16Char(ch, ch2);
            ch = static_cast(((ch16 >= 0x80) ? 0xff : ch16));
        }

        // Handling of newlines..
        if (ch == '\r')
            ch = '\n';

        return ch;
    }

    void ungetChar()
    {
        unget();
        if (isUtf16)
            unget();
    }

protected:
    void init() {
        // initialize since we use peek() in getAndSkipBOM()
        isUtf16 = false;
        bom = getAndSkipBOM();
        isUtf16 = (bom == 0xfeff || bom == 0xfffe);
    }

private:
    inline int makeUtf16Char(const unsigned char ch, const unsigned char ch2) const
    {
        return (bom == 0xfeff) ? (chpush_back(e);
    }
}

simplecpp::TokenList::TokenList(const TokenList &other) : frontToken(nullptr), backToken(nullptr), files(other.files)
{
    *this = other;
}

#if __cplusplus >= 201103L
simplecpp::TokenList::TokenList(TokenList &&other) : frontToken(nullptr), backToken(nullptr), files(other.files)
{
    *this = std::move(other);
}
#endif

simplecpp::TokenList::~TokenList()
{
    clear();
}

simplecpp::TokenList &simplecpp::TokenList::operator=(const TokenList &other)
{
    if (this != &other) {
        clear();
        files = other.files;
        for (const Token *tok = other.cfront(); tok; tok = tok->next)
            push_back(new Token(*tok));
        sizeOfType = other.sizeOfType;
    }
    return *this;
}

#if __cplusplus >= 201103L
simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other)
{
    if (this != &other) {
        clear();
        frontToken = other.frontToken;
        other.frontToken = nullptr;
        backToken = other.backToken;
        other.backToken = nullptr;
        files = other.files;
        sizeOfType = std::move(other.sizeOfType);
    }
    return *this;
}
#endif

void simplecpp::TokenList::clear()
{
    backToken = nullptr;
    while (frontToken) {
        Token * const next = frontToken->next;
        delete frontToken;
        frontToken = next;
    }
    sizeOfType.clear();
}

void simplecpp::TokenList::push_back(Token *tok)
{
    if (!frontToken)
        frontToken = tok;
    else
        backToken->next = tok;
    tok->previous = backToken;
    backToken = tok;
}

void simplecpp::TokenList::dump() const
{
    std::cout location.line < loc.line || tok->location.fileIndex != loc.fileIndex) {
            ret location;
        }

        while (tok->location.line > loc.line) {
            ret previous, tok))
            ret str());
    }

    return ret.str();
}

static bool isNameChar(unsigned char ch)
{
    return std::isalnum(ch) || ch == '_' || ch == '$';
}

static std::string escapeString(const std::string &str)
{
    std::ostringstream ostr;
    ostr name && isStringLiteralPrefix(cback()->str()) &&
                ((cback()->location.col + cback()->str().size()) == location.col) &&
                (cback()->location.line == location.line)) {
                prefix = cback()->str();
            }
            // C++11 raw string literal
            if (ch == '\"' && !prefix.empty() && *cback()->str().rbegin() == 'R') {
                std::string delim;
                currentToken = ch;
                prefix.resize(prefix.size() - 1);
                ch = stream.readChar();
                while (stream.good() && ch != '(' && ch != '\n') {
                    delim += ch;
                    ch = stream.readChar();
                }
                if (!stream.good() || ch == '\n') {
                    if (outputList) {
                        Output err(files);
                        err.type = Output::SYNTAX_ERROR;
                        err.location = location;
                        err.msg = "Invalid newline in raw string delimiter.";
                        outputList->push_back(err);
                    }
                    return;
                }
                const std::string endOfRawString(')' + delim + currentToken);
                while (stream.good() && !(endsWith(currentToken, endOfRawString) && currentToken.size() > 1))
                    currentToken += stream.readChar();
                if (!endsWith(currentToken, endOfRawString)) {
                    if (outputList) {
                        Output err(files);
                        err.type = Output::SYNTAX_ERROR;
                        err.location = location;
                        err.msg = "Raw string missing terminating delimiter.";
                        outputList->push_back(err);
                    }
                    return;
                }
                currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U);
                currentToken = escapeString(currentToken);
                currentToken.insert(0, prefix);
                back()->setstr(currentToken);
                location.adjust(currentToken);
                if (currentToken.find_first_of("\r\n") == std::string::npos)
                    location.col += 2 + 2 * delim.size();
                else
                    location.col += 1 + delim.size();

                continue;
            }

            currentToken = readUntil(stream,location,ch,ch,outputList);
            if (currentToken.size() < 2U)
                // Error is reported by readUntil()
                return;

            std::string s = currentToken;
            std::string::size_type pos;
            int newlines = 0;
            while ((pos = s.find_first_of("\r\n")) != std::string::npos) {
                s.erase(pos,1);
                newlines++;
            }

            if (prefix.empty())
                push_back(new Token(s, location, std::isspace(stream.peekChar()))); // push string without newlines
            else
                back()->setstr(prefix + s);

            if (newlines > 0 ) {
                const Token * const llTok = lastLineTok();
                if (llTok && llTok->op == '#' && llTok->next && (llTok->next->str() == "define" || llTok->next->str() == "pragma") && llTok->next->next) {
                    multiline += newlines;
                    location.adjust(s);
                    continue;
                }
            }

            location.adjust(currentToken);
            continue;
        }

        else {
            currentToken += ch;
        }

        if (*currentToken.begin() == '= 0 && start) {
                    if (start->op == ')')
                        ++indentlevel;
                    else if (start->op == '(')
                        --indentlevel;
                    else if (start->isOneOf(";{}"))
                        break;
                    start = start->previous;
                }
                if (indentlevel == -1 && start) {
                    const Token * const ftok = start;
                    bool isFuncDecl = ftok->name;
                    while (isFuncDecl) {
                        if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&')
                            isFuncDecl = false;
                        if (!start->previous)
                            break;
                        if (start->previous->isOneOf(";{}:"))
                            break;
                        start = start->previous;
                    }
                    isFuncDecl &= start != ftok && start->name;
                    if (isFuncDecl) {
                        // TODO: we could loop through the parameters here and check if they are correct.
                        continue;
                    }
                }
            }
            tok->setstr(tok->str() + "=");
            deleteToken(tok->next);
        } else if ((tok->op == '|' || tok->op == '&') && tok->op == tok->next->op) {
            tok->setstr(tok->str() + tok->next->str());
            deleteToken(tok->next);
        } else if (tok->op == ':' && tok->next->op == ':') {
            tok->setstr(tok->str() + tok->next->str());
            deleteToken(tok->next);
        } else if (tok->op == '-' && tok->next->op == '>') {
            tok->setstr(tok->str() + tok->next->str());
            deleteToken(tok->next);
        } else if ((tok->op == '') && tok->op == tok->next->op) {
            tok->setstr(tok->str() + tok->next->str());
            deleteToken(tok->next);
            if (tok->next && tok->next->op == '=' && tok->next->next && tok->next->next->op != '=') {
                tok->setstr(tok->str() + tok->next->str());
                deleteToken(tok->next);
            }
        } else if ((tok->op == '+' || tok->op == '-') && tok->op == tok->next->op) {
            if (tok->location.col + 1U != tok->next->location.col)
                continue;
            if (tok->previous && tok->previous->number)
                continue;
            if (tok->next->next && tok->next->next->number)
                continue;
            tok->setstr(tok->str() + tok->next->str());
            deleteToken(tok->next);
        }
    }
}

static const std::string COMPL("compl");
static const std::string NOT("not");
void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok)
{
    for (; tok && tok->op != ')'; tok = tok->next) {
        // "not" might be !
        if (isAlternativeUnaryOp(tok, NOT))
            tok->op = '!';
        // "compl" might be ~
        else if (isAlternativeUnaryOp(tok, COMPL))
            tok->op = '~';

        if (tok->op == '!' && tok->next && tok->next->number) {
            tok->setstr(tok->next->str() == "0" ? "1" : "0");
            deleteToken(tok->next);
        } else if (tok->op == '~' && tok->next && tok->next->number) {
            tok->setstr(toString(~stringToLL(tok->next->str())));
            deleteToken(tok->next);
        } else {
            if (tok->previous && (tok->previous->number || tok->previous->name))
                continue;
            if (!tok->next || !tok->next->number)
                continue;
            switch (tok->op) {
            case '+':
                tok->setstr(tok->next->str());
                deleteToken(tok->next);
                break;
            case '-':
                tok->setstr(tok->op + tok->next->str());
                deleteToken(tok->next);
                break;
            }
        }
    }
}

void simplecpp::TokenList::constFoldMulDivRem(Token *tok)
{
    for (; tok && tok->op != ')'; tok = tok->next) {
        if (!tok->previous || !tok->previous->number)
            continue;
        if (!tok->next || !tok->next->number)
            continue;

        long long result;
        if (tok->op == '*')
            result = (stringToLL(tok->previous->str()) * stringToLL(tok->next->str()));
        else if (tok->op == '/' || tok->op == '%') {
            const long long rhs = stringToLL(tok->next->str());
            if (rhs == 0)
                throw std::overflow_error("division/modulo by zero");
            const long long lhs = stringToLL(tok->previous->str());
            if (rhs == -1 && lhs == std::numeric_limits::min())
                throw std::overflow_error("division overflow");
            if (tok->op == '/')
                result = (lhs / rhs);
            else
                result = (lhs % rhs);
        } else
            continue;

        tok = tok->previous;
        tok->setstr(toString(result));
        deleteToken(tok->next);
        deleteToken(tok->next);
    }
}

void simplecpp::TokenList::constFoldAddSub(Token *tok)
{
    for (; tok && tok->op != ')'; tok = tok->next) {
        if (!tok->previous || !tok->previous->number)
            continue;
        if (!tok->next || !tok->next->number)
            continue;

        long long result;
        if (tok->op == '+')
            result = stringToLL(tok->previous->str()) + stringToLL(tok->next->str());
        else if (tok->op == '-')
            result = stringToLL(tok->previous->str()) - stringToLL(tok->next->str());
        else
            continue;

        tok = tok->previous;
        tok->setstr(toString(result));
        deleteToken(tok->next);
        deleteToken(tok->next);
    }
}

void simplecpp::TokenList::constFoldShift(Token *tok)
{
    for (; tok && tok->op != ')'; tok = tok->next) {
        if (!tok->previous || !tok->previous->number)
            continue;
        if (!tok->next || !tok->next->number)
            continue;

        long long result;
        if (tok->str() == ">")
            result = stringToLL(tok->previous->str()) >> stringToLL(tok->next->str());
        else
            continue;

        tok = tok->previous;
        tok->setstr(toString(result));
        deleteToken(tok->next);
        deleteToken(tok->next);
    }
}

static const std::string NOTEQ("not_eq");
void simplecpp::TokenList::constFoldComparison(Token *tok)
{
    for (; tok && tok->op != ')'; tok = tok->next) {
        if (isAlternativeBinaryOp(tok,NOTEQ))
            tok->setstr("!=");

        if (!tok->startsWithOneOf("=!"))
            continue;
        if (!tok->previous || !tok->previous->number)
            continue;
        if (!tok->next || !tok->next->number)
            continue;

        int result;
        if (tok->str() == "==")
            result = (stringToLL(tok->previous->str()) == stringToLL(tok->next->str()));
        else if (tok->str() == "!=")
            result = (stringToLL(tok->previous->str()) != stringToLL(tok->next->str()));
        else if (tok->str() == ">")
            result = (stringToLL(tok->previous->str()) > stringToLL(tok->next->str()));
        else if (tok->str() == ">=")
            result = (stringToLL(tok->previous->str()) >= stringToLL(tok->next->str()));
        else if (tok->str() == "";
            const bool canBeConcatenatedStringOrChar = isStringLiteral_(A->str()) || isCharLiteral_(A->str());
            if (!A->name && !A->number && A->op != ',' && !A->str().empty() && !canBeConcatenatedWithEqual && !canBeConcatenatedStringOrChar)
                throw invalidHashHash::unexpectedToken(tok->location, name(), A);

            Token * const B = tok->next->next;
            if (!B->name && !B->number && B->op && !B->isOneOf("#="))
                throw invalidHashHash::unexpectedToken(tok->location, name(), B);

            if ((canBeConcatenatedWithEqual && B->op != '=') ||
                (!canBeConcatenatedWithEqual && B->op == '='))
                throw invalidHashHash::cannotCombine(tok->location, name(), A, B);

            // Superficial check; more in-depth would in theory be possible _after_ expandArg
            if (canBeConcatenatedStringOrChar && (B->number || !B->name))
                throw invalidHashHash::cannotCombine(tok->location, name(), A, B);

            TokenList tokensB(files);
            const Token *nextTok = B->next;

            if (canBeConcatenatedStringOrChar) {
                // It seems clearer to handle this case separately even though the code is similar-ish, but we don't want to merge here.
                // TODO The question is whether the ## or varargs may still apply, and how to provoke?
                if (expandArg(&tokensB, B, parametertokens)) {
                    for (Token *b = tokensB.front(); b; b = b->next)
                        b->location = loc;
                } else {
                    tokensB.push_back(new Token(*B));
                    tokensB.back()->location = loc;
                }
                output->takeTokens(tokensB);
            } else {
                std::string strAB;

                const bool varargs = variadic && !args.empty() && B->str() == args[args.size()-1U];

                if (expandArg(&tokensB, B, parametertokens)) {
                    if (tokensB.empty())
                        strAB = A->str();
                    else if (varargs && A->op == ',') {
                        strAB = ",";
                    } else {
                        strAB = A->str() + tokensB.cfront()->str();
                        tokensB.deleteToken(tokensB.front());
                    }
                } else {
                    strAB = A->str() + B->str();
                }

                // producing universal character is undefined behavior
                if (A->previous && A->previous->str() == "\\") {
                    if (strAB[0] == 'u' && strAB.size() == 5)
                        throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB);
                    if (strAB[0] == 'U' && strAB.size() == 9)
                        throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB);
                }

                if (varargs && tokensB.empty() && tok->previous->str() == ",")
                    output->deleteToken(A);
                else if (strAB != "," && macros.find(strAB) == macros.end()) {
                    A->setstr(strAB);
                    for (Token *b = tokensB.front(); b; b = b->next)
                        b->location = loc;
                    output->takeTokens(tokensB);
                } else if (sameline(B, nextTok) && sameline(B, nextTok->next) && nextTok->op == '#' && nextTok->next->op == '#') {
                    TokenList output2(files);
                    output2.push_back(new Token(strAB, tok->location));
                    nextTok = expandHashHash(&output2, loc, nextTok, macros, expandedmacros, parametertokens);
                    output->deleteToken(A);
                    output->takeTokens(output2);
                } else {
                    output->deleteToken(A);
                    TokenList tokens(files);
                    tokens.push_back(new Token(strAB, tok->location));
                    // for function like macros, push the (...)
                    if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') {
                        const MacroMap::const_iterator it = macros.find(strAB);
                        if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) {
                            const Token * const tok2 = appendTokens(&tokens, loc, B->next, macros, expandedmacros, parametertokens);
                            if (tok2)
                                nextTok = tok2->next;
                        }
                    }
                    expandToken(output, loc, tokens.cfront(), macros, expandedmacros, parametertokens);
                    for (Token *b = tokensB.front(); b; b = b->next)
                        b->location = loc;
                    output->takeTokens(tokensB);
                }
            }

            return nextTok;
        }

        static bool isReplaced(const std::set &expandedmacros) {
            // return true if size > 1
            std::set::const_iterator it = expandedmacros.begin();
            if (it == expandedmacros.end())
                return false;
            ++it;
            return (it != expandedmacros.end());
        }

        /** name token in definition */
        const Token *nameTokDef;

        /** arguments for macro */
        std::vector args;

        /** first token in replacement string */
        const Token *valueToken;

        /** token after replacement string */
        const Token *endToken;

        /** files */
        std::vector &files;

        /** this is used for -D where the definition is not seen anywhere in code */
        TokenList tokenListDefine;

        /** usage of this macro */
        mutable std::list usageList;

        /** is macro variadic? */
        bool variadic;

        /** was the value of this macro actually defined in the code? */
        bool valueDefinedInCode_;
    };
}

namespace simplecpp {

#ifdef __CYGWIN__
    bool startsWith(const std::string &str, const std::string &s)
    {
        return (str.size() >= s.size() && str.compare(0, s.size(), s) == 0);
    }

    std::string convertCygwinToWindowsPath(const std::string &cygwinPath)
    {
        std::string windowsPath;

        std::string::size_type pos = 0;
        if (cygwinPath.size() >= 11 && startsWith(cygwinPath, "/cygdrive/")) {
            const unsigned char driveLetter = cygwinPath[10];
            if (std::isalpha(driveLetter)) {
                if (cygwinPath.size() == 11) {
                    windowsPath = toupper(driveLetter);
                    windowsPath += ":\\";   // volume root directory
                    pos = 11;
                } else if (cygwinPath[11] == '/') {
                    windowsPath = toupper(driveLetter);
                    windowsPath += ":";
                    pos = 11;
                }
            }
        }

        for (; pos < cygwinPath.size(); ++pos) {
            unsigned char c = cygwinPath[pos];
            if (c == '/')
                c = '\\';
            windowsPath += c;
        }

        return windowsPath;
    }
#endif
}

#ifdef SIMPLECPP_WINDOWS

#if __cplusplus >= 201103L
using MyMutex = std::mutex;
template
using MyLock = std::lock_guard;
#else
class MyMutex {
public:
    MyMutex() {
        InitializeCriticalSection(&m_criticalSection);
    }

    ~MyMutex() {
        DeleteCriticalSection(&m_criticalSection);
    }

    CRITICAL_SECTION* lock() {
        return &m_criticalSection;
    }
private:
    CRITICAL_SECTION m_criticalSection;
};

template
class MyLock {
public:
    explicit MyLock(T& m)
        : m_mutex(m) {
        EnterCriticalSection(m_mutex.lock());
    }

    ~MyLock() {
        LeaveCriticalSection(m_mutex.lock());
    }

private:
    MyLock& operator=(const MyLock&);
    MyLock(const MyLock&);

    T& m_mutex;
};
#endif

class RealFileNameMap {
public:
    RealFileNameMap() {}

    bool getCacheEntry(const std::string& path, std::string& returnPath) {
        MyLock lock(m_mutex);

        const std::map::iterator it = m_fileMap.find(path);
        if (it != m_fileMap.end()) {
            returnPath = it->second;
            return true;
        }
        return false;
    }

    void addToCache(const std::string& path, const std::string& actualPath) {
        MyLock lock(m_mutex);
        m_fileMap[path] = actualPath;
    }

private:
    std::map m_fileMap;
    MyMutex m_mutex;
};

static RealFileNameMap realFileNameMap;

static bool realFileName(const std::string &f, std::string &result)
{
    // are there alpha characters in last subpath?
    bool alpha = false;
    for (std::string::size_type pos = 1; pos = 3 && path[0] > 0 && std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/'))
        return true;
    return path.length() > 1U && (path[0] == '/' || path[0] == '\\');
}
#else
#define realFilename(f)  f

static bool isAbsolutePath(const std::string &path)
{
    return path.length() > 1U && path[0] == '/';
}
#endif

namespace simplecpp {
    /**
     * perform path simplifications for . and ..
     */
    std::string simplifyPath(std::string path)
    {
        if (path.empty())
            return path;

        std::string::size_type pos;

        // replace backslash separators
        std::replace(path.begin(), path.end(), '\\', '/');

        const bool unc(path.compare(0,2,"//") == 0);

        // replace "//" with "/"
        pos = 0;
        while ((pos = path.find("//",pos)) != std::string::npos) {
            path.erase(pos,1);
        }

        // remove "./"
        pos = 0;
        while ((pos = path.find("./",pos)) != std::string::npos) {
            if (pos == 0 || path[pos - 1U] == '/')
                path.erase(pos,2);
            else
                pos += 2;
        }

        // remove trailing dot if path ends with "/."
        if (endsWith(path,"/."))
            path.erase(path.size()-1);

        // simplify ".."
        pos = 1; // don't simplify ".." if path starts with that
        while ((pos = path.find("/..", pos)) != std::string::npos) {
            // not end of path, then string must be "/../"
            if (pos + 3 < path.size() && path[pos + 3] != '/') {
                ++pos;
                continue;
            }
            // get previous subpath
            std::string::size_type pos1 = path.rfind('/', pos - 1U);
            if (pos1 == std::string::npos) {
                pos1 = 0;
            } else {
                pos1 += 1U;
            }
            const std::string previousSubPath = path.substr(pos1, pos - pos1);
            if (previousSubPath == "..") {
                // don't simplify
                ++pos;
            } else {
                // remove previous subpath and ".."
                path.erase(pos1, pos - pos1 + 4);
                if (path.empty())
                    path = ".";
                // update pos
                pos = (pos1 == 0) ? 1 : (pos1 - 1);
            }
        }

        // Remove trailing '/'?
        //if (path.size() > 1 && endsWith(path, "/"))
        //    path.erase(path.size()-1);

        if (unc)
            path = '/' + path;

        // cppcheck-suppress duplicateExpressionTernary - platform-dependent implementation
        return strpbrk(path.c_str(), "*?") == nullptr ? realFilename(path) : path;
    }
}

/** Evaluate sizeof(type) */
static void simplifySizeof(simplecpp::TokenList &expr, const std::map &sizeOfType)
{
    for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
        if (tok->str() != "sizeof")
            continue;
        simplecpp::Token *tok1 = tok->next;
        if (!tok1) {
            throw std::runtime_error("missing sizeof argument");
        }
        simplecpp::Token *tok2 = tok1->next;
        if (!tok2) {
            throw std::runtime_error("missing sizeof argument");
        }
        if (tok1->op == '(') {
            tok1 = tok1->next;
            while (tok2->op != ')') {
                tok2 = tok2->next;
                if (!tok2) {
                    throw std::runtime_error("invalid sizeof expression");
                }
            }
        }

        std::string type;
        for (simplecpp::Token *typeToken = tok1; typeToken != tok2; typeToken = typeToken->next) {
            if ((typeToken->str() == "unsigned" || typeToken->str() == "signed") && typeToken->next->name)
                continue;
            if (typeToken->str() == "*" && type.find('*') != std::string::npos)
                continue;
            if (!type.empty())
                type += ' ';
            type += typeToken->str();
        }

        const std::map::const_iterator it = sizeOfType.find(type);
        if (it != sizeOfType.end())
            tok->setstr(toString(it->second));
        else
            continue;

        tok2 = tok2->next;
        while (tok->next != tok2)
            expr.deleteToken(tok->next);
    }
}

/** Evaluate __has_include(file) */
static bool isCpp17OrLater(const simplecpp::DUI &dui)
{
    const std::string std_ver = simplecpp::getCppStdString(dui.std);
    return !std_ver.empty() && (std_ver >= "201703L");
}

static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader);
static void simplifyHasInclude(simplecpp::TokenList &expr, const simplecpp::DUI &dui)
{
    if (!isCpp17OrLater(dui))
        return;

    for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
        if (tok->str() != HAS_INCLUDE)
            continue;
        simplecpp::Token *tok1 = tok->next;
        if (!tok1) {
            throw std::runtime_error("missing __has_include argument");
        }
        simplecpp::Token *tok2 = tok1->next;
        if (!tok2) {
            throw std::runtime_error("missing __has_include argument");
        }
        if (tok1->op == '(') {
            tok1 = tok1->next;
            while (tok2->op != ')') {
                tok2 = tok2->next;
                if (!tok2) {
                    throw std::runtime_error("invalid __has_include expression");
                }
            }
        }

        const std::string &sourcefile = tok->location.file();
        const bool systemheader = (tok1 && tok1->op == '') {
                tok3 = tok3->next;
                if (!tok3) {
                    throw std::runtime_error("invalid __has_include expression");
                }
            }

            for (simplecpp::Token *headerToken = tok1->next; headerToken != tok3; headerToken = headerToken->next)
                header += headerToken->str();
            // cppcheck-suppress selfAssignment - platform-dependent implementation
            header = realFilename(header);
        }
        else {
            header = realFilename(tok1->str().substr(1U, tok1->str().size() - 2U));
        }
        std::ifstream f;
        const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader);
        tok->setstr(header2.empty() ? "0" : "1");

        tok2 = tok2->next;
        while (tok->next != tok2)
            expr.deleteToken(tok->next);
    }
}

static const char * const altopData[] = {"and","or","bitand","bitor","compl","not","not_eq","xor"};
static const std::set altop(&altopData[0], &altopData[8]);
static void simplifyName(simplecpp::TokenList &expr)
{
    for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) {
        if (tok->name) {
            if (altop.find(tok->str()) != altop.end()) {
                bool alt;
                if (tok->str() == "not" || tok->str() == "compl") {
                    alt = isAlternativeUnaryOp(tok,tok->str());
                } else {
                    alt = isAlternativeBinaryOp(tok,tok->str());
                }
                if (alt)
                    continue;
            }
            tok->setstr("0");
        }
    }
}

/*
 * Reads at least minlen and at most maxlen digits (inc. prefix) in base base
 * from s starting at position pos and converts them to a
 * unsigned long long value, updating pos to point to the first
 * unused element of s.
 * Returns ULLONG_MAX if the result is not representable and
 * throws if the above requirements were not possible to satisfy.
 */
static unsigned long long stringToULLbounded(
    const std::string& s,
    std::size_t& pos,
    int base = 0,
    std::ptrdiff_t minlen = 1,
    std::size_t maxlen = std::string::npos
)
{
    const std::string sub = s.substr(pos, maxlen);
    const char * const start = sub.c_str();
    char* end;
    const unsigned long long value = std::strtoull(start, &end, base);
    pos += end - start;
    if (end - start < minlen)
        throw std::runtime_error("expected digit");
    return value;
}

/* Converts character literal (including prefix, but not ud-suffix)
 * to long long value.
 *
 * Assumes ASCII-compatible single-byte encoded str for narrow literals
 * and UTF-8 otherwise.
 *
 * For target assumes
 * - execution character set encoding matching str
 * - UTF-32 execution wide-character set encoding
 * - requirements for __STDC_UTF_16__, __STDC_UTF_32__ and __STDC_ISO_10646__ satisfied
 * - char16_t is 16bit wide
 * - char32_t is 32bit wide
 * - wchar_t is 32bit wide and unsigned
 * - matching char signedness to host
 * - matching sizeof(int) to host
 *
 * For host assumes
 * - ASCII-compatible execution character set
 *
 * For host and target assumes
 * - CHAR_BIT == 8
 * - two's complement
 *
 * Implements multi-character narrow literals according to GCC's behavior,
 * except multi code unit universal character names are not supported.
 * Multi-character wide literals are not supported.
 * Limited support of universal character names for non-UTF-8 execution character set encodings.
 */
long long simplecpp::characterLiteralToLL(const std::string& str)
{
    // default is wide/utf32
    bool narrow = false;
    bool utf8 = false;
    bool utf16 = false;

    std::size_t pos;

    if (!str.empty() && str[0] == '\'') {
        narrow = true;
        pos = 1;
    } else if (str.size() >= 2 && str[0] == 'u' && str[1] == '\'') {
        utf16 = true;
        pos = 2;
    } else if (str.size() >= 3 && str[0] == 'u' && str[1] == '8' && str[2] == '\'') {
        utf8 = true;
        pos = 3;
    } else if (str.size() >= 2 && (str[0] == 'L' || str[0] == 'U') && str[1] == '\'') {
        pos = 2;
    } else
        throw std::runtime_error("expected a character literal");

    unsigned long long multivalue = 0;

    std::size_t nbytes = 0;

    while (pos + 1 < str.size()) {
        if (str[pos] == '\'' || str[pos] == '\n')
            throw std::runtime_error("raw single quotes and newlines not allowed in character literals");

        if (nbytes >= 1 && !narrow)
            throw std::runtime_error("multiple characters only supported in narrow character literals");

        unsigned long long value;

        if (str[pos] == '\\') {
            pos++;
            const char escape = str[pos++];

            if (pos >= str.size())
                throw std::runtime_error("unexpected end of character literal");

            switch (escape) {
            // obscure GCC extensions
            case '%':
            case '(':
            case '[':
            case '{':
            // standard escape sequences
            case '\'':
            case '"':
            case '?':
            case '\\':
                value = static_cast(escape);
                break;

            case 'a':
                value = static_cast('\a');
                break;
            case 'b':
                value = static_cast('\b');
                break;
            case 'f':
                value = static_cast('\f');
                break;
            case 'n':
                value = static_cast('\n');
                break;
            case 'r':
                value = static_cast('\r');
                break;
            case 't':
                value = static_cast('\t');
                break;
            case 'v':
                value = static_cast('\v');
                break;

            // GCC extension for ESC character
            case 'e':
            case 'E':
                value = static_cast('\x1b');
                break;

            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
                // octal escape sequences consist of 1 to 3 digits
                value = stringToULLbounded(str, --pos, 8, 1, 3);
                break;

            case 'x':
                // hexadecimal escape sequences consist of at least 1 digit
                value = stringToULLbounded(str, pos, 16);
                break;

            case 'u':
            case 'U': {
                // universal character names have exactly 4 or 8 digits
                const std::size_t ndigits = (escape == 'u' ? 4 : 8);
                value = stringToULLbounded(str, pos, 16, ndigits, ndigits);

                // UTF-8 encodes code points above 0x7f in multiple code units
                // code points above 0x10ffff are not allowed
                if (((narrow || utf8) && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff)
                    throw std::runtime_error("code point too large");

                if (value >= 0xd800 && value = 0x80) {
                // Assuming this is a UTF-8 encoded code point.
                // This decoder may not completely validate the input.
                // Noncharacters are neither rejected nor replaced.

                int additional_bytes;
                if (value >= 0xf5)  // higher values would result in code points above 0x10ffff
                    throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid");
                if (value >= 0xf0)
                    additional_bytes = 3;
                else if (value >= 0xe0)
                    additional_bytes = 2;
                else if (value >= 0xc2) // 0xc0 and 0xc1 are always overlong 2-bytes encodings
                    additional_bytes = 1;
                else
                    throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid");

                value &= (1 = str.size())
                        throw std::runtime_error("assumed UTF-8 encoded source, but character literal ends unexpectedly");

                    const unsigned char c = str[pos++];

                    if (((c >> 6) != 2)    // ensure c has form 0xb10xxxxxx
                        || (!value && additional_bytes == 1 && c < 0xa0)    // overlong 3-bytes encoding
                        || (!value && additional_bytes == 2 && c < 0x90))   // overlong 4-bytes encoding
                        throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid");

                    value = (value  0xffff) || value > 0x10ffff)
                    throw std::runtime_error("code point too large");
            }
        }

        if (((narrow || utf8) && value > std::numeric_limits::max()) || (utf16 && value >> 16) || value >> 32)
            throw std::runtime_error("numeric escape sequence too large");

        multivalue setstr(toString(simplecpp::characterLiteralToLL(tok->str())));
    }
}

static void simplifyComments(simplecpp::TokenList &expr)
{
    for (simplecpp::Token *tok = expr.front(); tok;) {
        simplecpp::Token * const d = tok;
        tok = tok->next;
        if (d->comment)
            expr.deleteToken(d);
    }
}

static long long evaluate(simplecpp::TokenList &expr, const simplecpp::DUI &dui, const std::map &sizeOfType)
{
    simplifyComments(expr);
    simplifySizeof(expr, sizeOfType);
    simplifyHasInclude(expr, dui);
    simplifyName(expr);
    simplifyNumbers(expr);
    expr.constFold();
    // TODO: handle invalid expressions
    return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str()) : 0LL;
}

static const simplecpp::Token *gotoNextLine(const simplecpp::Token *tok)
{
    const unsigned int line = tok->location.line;
    const unsigned int file = tok->location.fileIndex;
    while (tok && tok->location.line == line && tok->location.fileIndex == file)
        tok = tok->next;
    return tok;
}

#ifdef SIMPLECPP_WINDOWS

class NonExistingFilesCache {
public:
    NonExistingFilesCache() {}

    bool contains(const std::string& path) {
        MyLock lock(m_mutex);
        return (m_pathSet.find(path) != m_pathSet.end());
    }

    void add(const std::string& path) {
        MyLock lock(m_mutex);
        m_pathSet.insert(path);
    }

    void clear() {
        MyLock lock(m_mutex);
        m_pathSet.clear();
    }

private:
    std::set m_pathSet;
    MyMutex m_mutex;
};

static NonExistingFilesCache nonExistingFilesCache;

#endif

static std::string openHeader(std::ifstream &f, const std::string &path)
{
    std::string simplePath = simplecpp::simplifyPath(path);
#ifdef SIMPLECPP_WINDOWS
    if (nonExistingFilesCache.contains(simplePath))
        return "";  // file is known not to exist, skip expensive file open call
#endif
    f.open(simplePath.c_str());
    if (f.is_open())
        return simplePath;
#ifdef SIMPLECPP_WINDOWS
    nonExistingFilesCache.add(simplePath);
#endif
    return "";
}

static std::string getRelativeFileName(const std::string &sourcefile, const std::string &header)
{
    if (sourcefile.find_first_of("\\/") != std::string::npos)
        return simplecpp::simplifyPath(sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header);
    return simplecpp::simplifyPath(header);
}

static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header)
{
    return openHeader(f, getRelativeFileName(sourcefile, header));
}

static std::string getIncludePathFileName(const std::string &includePath, const std::string &header)
{
    std::string path = includePath;
    if (!path.empty() && path[path.size()-1U]!='/' && path[path.size()-1U]!='\\')
        path += '/';
    return path + header;
}

static std::string openHeaderIncludePath(std::ifstream &f, const simplecpp::DUI &dui, const std::string &header)
{
    for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) {
        std::string simplePath = openHeader(f, getIncludePathFileName(*it, header));
        if (!simplePath.empty())
            return simplePath;
    }
    return "";
}

static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader)
{
    if (isAbsolutePath(header))
        return openHeader(f, header);

    std::string ret;

    if (systemheader) {
        ret = openHeaderIncludePath(f, dui, header);
        return ret;
    }

    ret = openHeaderRelative(f, sourcefile, header);
    if (ret.empty())
        return openHeaderIncludePath(f, dui, header);
    return ret;
}

static std::string getFileName(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader)
{
    if (filedata.empty()) {
        return "";
    }
    if (isAbsolutePath(header)) {
        return (filedata.find(header) != filedata.end()) ? simplecpp::simplifyPath(header) : "";
    }

    if (!systemheader) {
        const std::string relativeFilename = getRelativeFileName(sourcefile, header);
        if (filedata.find(relativeFilename) != filedata.end())
            return relativeFilename;
    }

    for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) {
        std::string s = simplecpp::simplifyPath(getIncludePathFileName(*it, header));
        if (filedata.find(s) != filedata.end())
            return s;
    }

    if (systemheader && filedata.find(header) != filedata.end())
        return header;

    return "";
}

static bool hasFile(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader)
{
    return !getFileName(filedata, sourcefile, header, dui, systemheader).empty();
}

std::map simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector &filenames, const simplecpp::DUI &dui, simplecpp::OutputList *outputList)
{
#ifdef SIMPLECPP_WINDOWS
    if (dui.clearIncludeCache)
        nonExistingFilesCache.clear();
#endif

    std::map ret;

    std::list filelist;

    // -include files
    for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) {
        const std::string &filename = realFilename(*it);

        if (ret.find(filename) != ret.end())
            continue;

        std::ifstream fin(filename.c_str());
        if (!fin.is_open()) {
            if (outputList) {
                simplecpp::Output err(filenames);
                err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND;
                err.location = Location(filenames);
                err.msg = "Can not open include file '" + filename + "' that is explicitly included.";
                outputList->push_back(err);
            }
            continue;
        }
        fin.close();

        TokenList *tokenlist = new TokenList(filename, filenames, outputList);
        if (!tokenlist->front()) {
            delete tokenlist;
            continue;
        }

        if (dui.removeComments)
            tokenlist->removeComments();
        ret[filename] = tokenlist;
        filelist.push_back(tokenlist->front());
    }

    for (const Token *rawtok = rawtokens.cfront(); rawtok || !filelist.empty(); rawtok = rawtok ? rawtok->next : nullptr) {
        if (rawtok == nullptr) {
            rawtok = filelist.back();
            filelist.pop_back();
        }

        if (rawtok->op != '#' || sameline(rawtok->previousSkipComments(), rawtok))
            continue;

        rawtok = rawtok->nextSkipComments();
        if (!rawtok || rawtok->str() != INCLUDE)
            continue;

        const std::string &sourcefile = rawtok->location.file();

        const Token * const htok = rawtok->nextSkipComments();
        if (!sameline(rawtok, htok))
            continue;

        const bool systemheader = (htok->str()[0] == '') {
                    TokenString hdr;
                    // TODO: Sometimes spaces must be added in the string
                    // Somehow preprocessToken etc must be told that the location should be source location not destination location
                    for (const Token *tok = inc2.cfront(); tok; tok = tok->next) {
                        hdr += tok->str();
                    }
                    inc2.clear();
                    inc2.push_back(new Token(hdr, inc1.cfront()->location));
                    inc2.front()->op = '')
                                        header += tok->str();
                                    // cppcheck-suppress selfAssignment - platform-dependent implementation
                                    header = realFilename(header);
                                    if (tok && tok->op == '>')
                                        closingAngularBracket = true;
                                }
                                else {
                                    header = realFilename(tok->str().substr(1U, tok->str().size() - 2U));
                                    closingAngularBracket = true;
                                }
                                std::ifstream f;
                                const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader);
                                expr.push_back(new Token(header2.empty() ? "0" : "1", tok->location));
                            }
                            if (par)
                                tok = tok ? tok->next : nullptr;
                            if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')') || (!closingAngularBracket)) {
                                if (outputList) {
                                    Output out(rawtok->location.files);
                                    out.type = Output::SYNTAX_ERROR;
                                    out.location = rawtok->location;
                                    out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition";
                                    outputList->push_back(out);
                                }
                                output.clear();
                                return;
                            }
                            continue;
                        }

                        maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location);

                        const Token *tmp = tok;
                        if (!preprocessToken(expr, &tmp, macros, files, outputList)) {
                            output.clear();
                            return;
                        }
                        if (!tmp)
                            break;
                        tok = tmp->previous;
                    }
                    try {
                        if (ifCond) {
                            std::string E;
                            for (const simplecpp::Token *tok = expr.cfront(); tok; tok = tok->next)
                                E += (E.empty() ? "" : " ") + tok->str();
                            const long long result = evaluate(expr, dui, sizeOfType);
                            conditionIsTrue = (result != 0);
                            ifCond->push_back(IfCond(rawtok->location, E, result));
                        } else {
                            const long long result = evaluate(expr, dui, sizeOfType);
                            conditionIsTrue = (result != 0);
                        }
                    } catch (const std::exception &e) {
                        if (outputList) {
                            Output out(rawtok->location.files);
                            out.type = Output::SYNTAX_ERROR;
                            out.location = rawtok->location;
                            out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition";
                            if (e.what() && *e.what())
                                out.msg += std::string(", ") + e.what();
                            outputList->push_back(out);
                        }
                        output.clear();
                        return;
                    }
                }

                if (rawtok->str() != ELIF) {
                    // push a new ifstate..
                    if (ifstates.top() != True)
                        ifstates.push(AlwaysFalse);
                    else
                        ifstates.push(conditionIsTrue ? True : ElseIsTrue);
                } else if (ifstates.top() == True) {
                    ifstates.top() = AlwaysFalse;
                } else if (ifstates.top() == ElseIsTrue && conditionIsTrue) {
                    ifstates.top() = True;
                }
            } else if (rawtok->str() == ELSE) {
                ifstates.top() = (ifstates.top() == ElseIsTrue) ? True : AlwaysFalse;
            } else if (rawtok->str() == ENDIF) {
                ifstates.pop();
            } else if (rawtok->str() == UNDEF) {
                if (ifstates.top() == True) {
                    const Token *tok = rawtok->next;
                    while (sameline(rawtok,tok) && tok->comment)
                        tok = tok->next;
                    if (sameline(rawtok, tok))
                        macros.erase(tok->str());
                }
            } else if (ifstates.top() == True && rawtok->str() == PRAGMA && rawtok->next && rawtok->next->str() == ONCE && sameline(rawtok,rawtok->next)) {
                pragmaOnce.insert(rawtok->location.file());
            }
            rawtok = gotoNextLine(rawtok);
            continue;
        }

        if (ifstates.top() != True) {
            // drop code
            rawtok = gotoNextLine(rawtok);
            continue;
        }

        bool hash=false, hashhash=false;
        if (rawtok->op == '#' && sameline(rawtok,rawtok->next)) {
            if (rawtok->next->op != '#') {
                hash = true;
                rawtok = rawtok->next; // skip '#'
            } else if (sameline(rawtok,rawtok->next->next)) {
                hashhash = true;
                rawtok = rawtok->next->next; // skip '#' '#'
            }
        }

        const Location loc(rawtok->location);
        TokenList tokens(files);

        if (!preprocessToken(tokens, &rawtok, macros, files, outputList)) {
            output.clear();
            return;
        }

        if (hash || hashhash) {
            std::string s;
            for (const Token *hashtok = tokens.cfront(); hashtok; hashtok = hashtok->next)
                s += hashtok->str();
            if (hash)
                output.push_back(new Token('\"' + s + '\"', loc));
            else if (output.back())
                output.back()->setstr(output.cback()->str() + s);
            else
                output.push_back(new Token(s, loc));
        } else {
            output.takeTokens(tokens);
        }
    }

    if (macroUsage) {
        for (simplecpp::MacroMap::const_iterator macroIt = macros.begin(); macroIt != macros.end(); ++macroIt) {
            const Macro ¯o = macroIt->second;
            std::list usage = macro.usage();
            const std::list& temp = maybeUsedMacros[macro.name()];
            usage.insert(usage.end(), temp.begin(), temp.end());
            for (std::list::const_iterator usageIt = usage.begin(); usageIt != usage.end(); ++usageIt) {
                MacroUsage mu(usageIt->files, macro.valueDefinedInCode());
                mu.macroName = macro.name();
                mu.macroLocation = macro.defineLocation();
                mu.useLocation = *usageIt;
                macroUsage->push_back(mu);
            }
        }
    }
}

void simplecpp::cleanup(std::map &filedata)
{
    for (std::map::iterator it = filedata.begin(); it != filedata.end(); ++it)
        delete it->second;
    filedata.clear();
}

simplecpp::cstd_t simplecpp::getCStd(const std::string &std)
{
    if (std == "c90" || std == "c89" || std == "iso9899:1990" || std == "iso9899:199409" || std == "gnu90" || std == "gnu89")
        return C89;
    if (std == "c99" || std == "c9x" || std == "iso9899:1999" || std == "iso9899:199x" || std == "gnu99"|| std == "gnu9x")
        return C99;
    if (std == "c11" || std == "c1x" || std == "iso9899:2011" || std == "gnu11" || std == "gnu1x")
        return C11;
    if (std == "c17" || std == "c18" || std == "iso9899:2017" || std == "iso9899:2018" || std == "gnu17"|| std == "gnu18")
        return C17;
    if (std == "c23" || std == "gnu23" || std == "c2x" || std == "gnu2x")
        return C23;
    return CUnknown;
}

std::string simplecpp::getCStdString(cstd_t std)
{
    switch (std)
    {
        case C89:
            // __STDC_VERSION__ is not set for C90 although the macro was added in the 1994 amendments
            return "";
        case C99:
            return "199901L";
        case C11:
            return "201112L";
        case C17:
            return "201710L";
        case C23:
            // supported by GCC 9+ and Clang 9+
            // Clang 9, 10, 11, 12, 13 return "201710L"
            // Clang 14, 15, 16, 17 return "202000L"
            // Clang 9, 10, 11, 12, 13, 14, 15, 16, 17 do not support "c23" and "gnu23"
            return "202311L";
        case CUnknown:
            return "";
    }
    return "";
}

std::string simplecpp::getCStdString(const std::string &std)
{
    return getCStdString(getCStd(std));
}

simplecpp::cppstd_t simplecpp::getCppStd(const std::string &std)
{
    if (std == "c++98" || std == "c++03" || std == "gnu++98" || std == "gnu++03")
        return CPP03;
    if (std == "c++11" || std == "gnu++11" || std == "c++0x" || std == "gnu++0x")
        return CPP11;
    if (std == "c++14" || std == "c++1y" || std == "gnu++14" || std == "gnu++1y")
        return CPP14;
    if (std == "c++17" || std == "c++1z" || std == "gnu++17" || std == "gnu++1z")
        return CPP17;
    if (std == "c++20" || std == "c++2a" || std == "gnu++20" || std == "gnu++2a")
        return CPP20;
    if (std == "c++23" || std == "c++2b" || std == "gnu++23" || std == "gnu++2b")
        return CPP23;
    if (std == "c++26" || std == "c++2c" || std == "gnu++26" || std == "gnu++2c")
        return CPP26;
    return CPPUnknown;
}

std::string simplecpp::getCppStdString(cppstd_t std)
{
    switch (std)
    {
        case CPP03:
            return "199711L";
        case CPP11:
            return "201103L";
        case CPP14:
            return "201402L";
        case CPP17:
            return "201703L";
        case CPP20:
            // GCC 10 returns "201703L" - correct in 11+
            return "202002L";
        case CPP23:
            // supported by GCC 11+ and Clang 12+
            // GCC 11, 12, 13 return "202100L"
            // Clang 12, 13, 14, 15, 16 do not support "c++23" and "gnu++23" and return "202101L"
            // Clang 17, 18 return "202302L"
            return "202302L";
        case CPP26:
            // supported by Clang 17+
            return "202400L";
        case CPPUnknown:
            return "";
    }
    return "";
}

std::string simplecpp::getCppStdString(const std::string &std)
{
    return getCppStdString(getCppStd(std));
}

#if (__cplusplus < 201103L) && !defined(__APPLE__)
#undef nullptr
#endif

Web Proxy Viewer  |  New URL  |  Original Page