Coverage Report

Created: 2023-09-25 06:15

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