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