Cppcheck
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
preprocessor.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2015 Daniel Marjamäki and Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 
20 #include "preprocessor.h"
21 #include "tokenize.h"
22 #include "token.h"
23 #include "path.h"
24 #include "errorlogger.h"
25 #include "settings.h"
26 
27 #include <algorithm>
28 #include <sstream>
29 #include <fstream>
30 #include <cstdlib>
31 #include <cctype>
32 #include <vector>
33 #include <set>
34 #include <stack>
35 
38 
39 char Preprocessor::macroChar = char(1);
40 
41 Preprocessor::Preprocessor(Settings& settings, ErrorLogger *errorLogger) : _settings(settings), _errorLogger(errorLogger)
42 {
43 
44 }
45 
46 void Preprocessor::writeError(const std::string &fileName, const unsigned int linenr, ErrorLogger *errorLogger, const std::string &errorType, const std::string &errorText)
47 {
48  if (!errorLogger)
49  return;
50 
51  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
52  ErrorLogger::ErrorMessage::FileLocation loc(fileName, linenr);
53  locationList.push_back(loc);
54  errorLogger->reportErr(ErrorLogger::ErrorMessage(locationList,
56  errorText,
57  errorType,
58  false));
59 }
60 
61 static unsigned char readChar(std::istream &istr, unsigned int bom)
62 {
63  unsigned char ch = (unsigned char)istr.get();
64 
65  // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the
66  // character is non-ASCII character then replace it with 0xff
67  if (bom == 0xfeff || bom == 0xfffe) {
68  const unsigned char ch2 = (unsigned char)istr.get();
69  const int ch16 = (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch);
70  ch = (unsigned char)((ch16 >= 0x80) ? 0xff : ch16);
71  }
72 
73  // Handling of newlines..
74  if (ch == '\r') {
75  ch = '\n';
76  if (bom == 0 && (char)istr.peek() == '\n')
77  (void)istr.get();
78  else if (bom == 0xfeff || bom == 0xfffe) {
79  int c1 = istr.get();
80  int c2 = istr.get();
81  int ch16 = (bom == 0xfeff) ? (c1<<8 | c2) : (c2<<8 | c1);
82  if (ch16 != '\n') {
83  istr.unget();
84  istr.unget();
85  }
86  }
87  }
88 
89  return ch;
90 }
91 
92 // Concatenates a list of strings, inserting a separator between parts
93 static std::string join(const std::set<std::string>& list, char separator)
94 {
95  std::string s;
96  for (std::set<std::string>::const_iterator it = list.begin(); it != list.end(); ++it) {
97  if (!s.empty())
98  s += separator;
99 
100  s += *it;
101  }
102  return s;
103 }
104 
105 // Removes duplicate string portions separated by the specified separator
106 static std::string unify(const std::string &s, char separator)
107 {
108  std::set<std::string> parts;
109 
110  std::string::size_type prevPos = 0;
111  for (std::string::size_type pos = 0; pos < s.length(); ++pos) {
112  if (s[pos] == separator) {
113  if (pos > prevPos)
114  parts.insert(s.substr(prevPos, pos - prevPos));
115  prevPos = pos + 1;
116  }
117  }
118  if (prevPos < s.length())
119  parts.insert(s.substr(prevPos));
120 
121  return join(parts, separator);
122 }
123 
124 
125 bool Preprocessor::cplusplus(const Settings *settings, const std::string &filename)
126 {
127  const bool undef = settings && settings->userUndefs.find("__cplusplus") != settings->userUndefs.end();
128  const bool cpplang = settings && settings->enforcedLang == Settings::CPP;
129  const bool cppfile = (!settings || settings->enforcedLang == Settings::None) && Path::isCPP(filename);
130  return (!undef && (cpplang || cppfile));
131 }
132 
133 /**
134  * Get cfgmap - a map of macro names and values
135  */
136 static std::map<std::string,std::string> getcfgmap(const std::string &cfg, const Settings *settings, const std::string &filename)
137 {
138  std::map<std::string, std::string> cfgmap;
139 
140  if (!cfg.empty()) {
141  std::string::size_type pos = 0;
142  for (;;) {
143  std::string::size_type pos2 = cfg.find_first_of(";=", pos);
144  if (pos2 == std::string::npos) {
145  cfgmap[cfg.substr(pos)] = "";
146  break;
147  }
148  if (cfg[pos2] == ';') {
149  cfgmap[cfg.substr(pos, pos2-pos)] = "";
150  } else {
151  std::string::size_type pos3 = pos2;
152  pos2 = cfg.find(';', pos2);
153  if (pos2 == std::string::npos) {
154  cfgmap[cfg.substr(pos, pos3-pos)] = cfg.substr(pos3 + 1);
155  break;
156  } else {
157  cfgmap[cfg.substr(pos, pos3-pos)] = cfg.substr(pos3 + 1, pos2 - pos3 - 1);
158  }
159  }
160  pos = pos2 + 1;
161  }
162  }
163 
164  if (cfgmap.find("__cplusplus") == cfgmap.end() && Preprocessor::cplusplus(settings,filename))
165  cfgmap["__cplusplus"] = "1";
166 
167  return cfgmap;
168 }
169 
170 
171 /** Just read the code into a string. Perform simple cleanup of the code */
172 std::string Preprocessor::read(std::istream &istr, const std::string &filename)
173 {
174  // The UTF-16 BOM is 0xfffe or 0xfeff.
175  unsigned int bom = 0;
176  if (istr.peek() >= 0xfe) {
177  bom = ((unsigned int)istr.get() << 8);
178  if (istr.peek() >= 0xfe)
179  bom |= (unsigned int)istr.get();
180  else
181  bom = 0; // allowed boms are 0/0xfffe/0xfeff
182  }
183 
184  if (_settings.terminated())
185  return "";
186 
188  return readpreprocessor(istr,bom);
189 
190  // ------------------------------------------------------------------------------------------
191  //
192  // handling <backslash><newline>
193  // when this is encountered the <backslash><newline> will be "skipped".
194  // on the next <newline>, extra newlines will be added
195  std::ostringstream code;
196  unsigned int newlines = 0;
197  for (unsigned char ch = readChar(istr,bom); istr.good(); ch = readChar(istr,bom)) {
198  // Replace assorted special chars with spaces..
199  if (((ch & 0x80) == 0) && (ch != '\n') && (std::isspace(ch) || std::iscntrl(ch)))
200  ch = ' ';
201 
202  // <backslash><newline>..
203  // for gcc-compatibility the trailing spaces should be ignored
204  // for vs-compatibility the trailing spaces should be kept
205  // See tickets #640 and #1869
206  // The solution for now is to have a compiler-dependent behaviour.
207  if (ch == '\\') {
208  unsigned char chNext;
209 
210  std::string spaces;
211 
212 #ifdef __GNUC__
213  // gcc-compatibility: ignore spaces
214  for (;; spaces += ' ') {
215  chNext = (unsigned char)istr.peek();
216  if (chNext != '\n' && chNext != '\r' &&
217  (std::isspace(chNext) || std::iscntrl(chNext))) {
218  // Skip whitespace between <backslash> and <newline>
219  (void)readChar(istr,bom);
220  continue;
221  }
222 
223  break;
224  }
225 #else
226  // keep spaces
227  chNext = (unsigned char)istr.peek();
228 #endif
229  if (chNext == '\n' || chNext == '\r') {
230  ++newlines;
231  (void)readChar(istr,bom); // Skip the "<backslash><newline>"
232  } else {
233  code << "\\" << spaces;
234  }
235  } else {
236  code << char(ch);
237 
238  // if there has been <backslash><newline> sequences, add extra newlines..
239  if (ch == '\n' && newlines > 0) {
240  code << std::string(newlines, '\n');
241  newlines = 0;
242  }
243  }
244  }
245 
246  // ------------------------------------------------------------------------------------------
247  //
248  // Remove all comments..
249  std::string result = removeComments(code.str(), filename);
250  if (_settings.terminated())
251  return "";
252  code.str("");
253 
254  // ------------------------------------------------------------------------------------------
255  //
256  // Clean up all preprocessor statements
257  result = preprocessCleanupDirectives(result);
258  if (_settings.terminated())
259  return "";
260 
261  // ------------------------------------------------------------------------------------------
262  //
263  // Clean up preprocessor #if statements with Parentheses
264  result = removeParentheses(result);
265  if (_settings.terminated())
266  return "";
267 
268  // Remove '#if 0' blocks
269  if (result.find("#if 0\n") != std::string::npos)
270  result = removeIf0(result);
271  if (_settings.terminated())
272  return "";
273 
274  return result;
275 }
276 
277 
278 /** read preprocessor statements */
279 std::string Preprocessor::readpreprocessor(std::istream &istr, const unsigned int bom)
280 {
281  enum { NEWLINE, SPACE, PREPROCESSOR, BACKSLASH, OTHER } state = NEWLINE;
282  std::ostringstream code;
283  unsigned int newlines = 1;
284  unsigned char chPrev = ' ';
285  for (unsigned char ch = readChar(istr,bom); istr.good(); ch = readChar(istr,bom)) {
286  // Replace assorted special chars with spaces..
287  if (((ch & 0x80) == 0) && (ch != '\n') && (std::isspace(ch) || std::iscntrl(ch)))
288  ch = ' ';
289 
290  if (ch == ' ' && chPrev == ' ')
291  continue;
292  if (state == PREPROCESSOR && chPrev == '/' && (ch == '/' || ch == '*'))
293  state = OTHER;
294  chPrev = ch;
295 
296  if (ch == '\n') {
297  if (state != BACKSLASH) {
298  state = NEWLINE;
299  code << std::string(newlines, '\n');
300  newlines = 1;
301  } else {
302  ++newlines;
303  state = PREPROCESSOR;
304  }
305  continue;
306  }
307 
308  switch (state) {
309  case NEWLINE:
310  if (ch==' ')
311  state = SPACE;
312  else if (ch == '#') {
313  state = PREPROCESSOR;
314  code << ch;
315  } else
316  state = OTHER;
317  break;
318  case SPACE:
319  if (ch == '#') {
320  state = PREPROCESSOR;
321  code << ch;
322  } else if (ch != ' ')
323  state = OTHER;
324  break;
325  case PREPROCESSOR:
326  code << ch;
327  if (ch == '\\')
328  state = BACKSLASH;
329  break;
330  case BACKSLASH:
331  code << ch;
332  if (ch != ' ')
333  state = PREPROCESSOR;
334  break;
335  case OTHER:
336  break;
337  };
338  }
339 
340  std::string result = preprocessCleanupDirectives(code.str());
341  result = removeParentheses(result);
342  return removeIf0(result);
343 }
344 
345 std::string Preprocessor::preprocessCleanupDirectives(const std::string &processedFile)
346 {
347  std::ostringstream code;
348  std::istringstream sstr(processedFile);
349 
350  std::string line;
351  while (std::getline(sstr, line)) {
352  // Trim lines..
353  if (!line.empty() && line[0] == ' ')
354  line.erase(0, line.find_first_not_of(" "));
355  if (!line.empty() && line.back() == ' ')
356  line.erase(line.find_last_not_of(" ") + 1);
357 
358  // Preprocessor
359  if (!line.empty() && line[0] == '#') {
360  enum {
361  ESC_NONE,
362  ESC_SINGLE,
363  ESC_DOUBLE
364  } escapeStatus = ESC_NONE;
365 
366  char prev = ' '; // hack to make it skip spaces between # and the directive
367  code << "#";
368  std::string::const_iterator i = line.begin();
369  ++i;
370 
371  // need space.. #if( => #if (
372  bool needSpace = true;
373  while (i != line.end()) {
374  // disable esc-mode
375  if (escapeStatus != ESC_NONE) {
376  if (prev != '\\' && escapeStatus == ESC_SINGLE && *i == '\'') {
377  escapeStatus = ESC_NONE;
378  }
379  if (prev != '\\' && escapeStatus == ESC_DOUBLE && *i == '"') {
380  escapeStatus = ESC_NONE;
381  }
382  } else {
383  // enable esc-mode
384  if (escapeStatus == ESC_NONE && *i == '"')
385  escapeStatus = ESC_DOUBLE;
386  if (escapeStatus == ESC_NONE && *i == '\'')
387  escapeStatus = ESC_SINGLE;
388  }
389  // skip double whitespace between arguments
390  if (escapeStatus == ESC_NONE && prev == ' ' && *i == ' ') {
391  ++i;
392  continue;
393  }
394  // Convert #if( to "#if ("
395  if (escapeStatus == ESC_NONE) {
396  if (needSpace) {
397  if (*i == '(' || *i == '!')
398  code << " ";
399  else if (!std::isalpha((unsigned char)*i))
400  needSpace = false;
401  }
402  if (*i == '#')
403  needSpace = true;
404  }
405  code << *i;
406  if (escapeStatus != ESC_NONE && prev == '\\' && *i == '\\') {
407  prev = ' ';
408  } else {
409  prev = *i;
410  }
411  ++i;
412  }
413  if (escapeStatus != ESC_NONE) {
414  // unmatched quotes.. compiler should probably complain about this..
415  }
416  } else {
417  // Do not mess with regular code..
418  code << line;
419  }
420  code << (sstr.eof()?"":"\n");
421  }
422 
423  return code.str();
424 }
425 
426 static bool hasbom(const std::string &str)
427 {
428  return bool(str.size() >= 3 &&
429  static_cast<unsigned char>(str[0]) == 0xef &&
430  static_cast<unsigned char>(str[1]) == 0xbb &&
431  static_cast<unsigned char>(str[2]) == 0xbf);
432 }
433 
434 
435 // This wrapper exists because Sun's CC does not allow a static_cast
436 // from extern "C" int(*)(int) to int(*)(int).
437 static int tolowerWrapper(int c)
438 {
439  return std::tolower(c);
440 }
441 
442 
443 static bool isFallThroughComment(std::string comment)
444 {
445  // convert comment to lower case without whitespace
446  for (std::string::iterator i = comment.begin(); i != comment.end();) {
447  if (std::isspace(static_cast<unsigned char>(*i)))
448  i = comment.erase(i);
449  else
450  ++i;
451  }
452  std::transform(comment.begin(), comment.end(), comment.begin(), tolowerWrapper);
453 
454  return comment.find("fallthr") != std::string::npos ||
455  comment.find("fallsthr") != std::string::npos ||
456  comment.find("fall-thr") != std::string::npos ||
457  comment.find("dropthr") != std::string::npos ||
458  comment.find("passthr") != std::string::npos ||
459  comment.find("nobreak") != std::string::npos ||
460  comment == "fall";
461 }
462 
463 std::string Preprocessor::removeComments(const std::string &str, const std::string &filename)
464 {
465  // For the error report
466  unsigned int lineno = 1;
467 
468  // handling <backslash><newline>
469  // when this is encountered the <backslash><newline> will be "skipped".
470  // on the next <newline>, extra newlines will be added
471  unsigned int newlines = 0;
472  std::ostringstream code;
473  unsigned char previous = 0;
474  bool inPreprocessorLine = false;
475  std::vector<std::string> suppressionIDs;
476  const bool detectFallThroughComments = _settings.experimental && _settings.isEnabled("style");
477  bool fallThroughComment = false;
478 
479  for (std::string::size_type i = hasbom(str) ? 3U : 0U; i < str.length(); ++i) {
480  unsigned char ch = static_cast<unsigned char>(str[i]);
481  if (ch & 0x80) {
482  std::ostringstream errmsg;
483  errmsg << "(character code = 0x" << std::hex << (int(ch) & 0xff) << ")";
484  std::string info = errmsg.str();
485  errmsg.str("");
486  errmsg << "The code contains unhandled characters " << info << ". Checking continues, but do not expect valid results.\n"
487  << "The code contains characters that are unhandled " << info << ". Neither unicode nor extended ASCII are supported. Checking continues, but do not expect valid results.";
488  writeError(filename, lineno, _errorLogger, "unhandledCharacters", errmsg.str());
489  }
490 
491  if (_settings.terminated())
492  return "";
493 
494  // First skip over any whitespace that may be present
495  if (std::isspace(ch)) {
496  if (ch == ' ' && previous == ' ') {
497  // Skip double white space
498  } else {
499  code << char(ch);
500  previous = ch;
501  }
502 
503  // if there has been <backslash><newline> sequences, add extra newlines..
504  if (ch == '\n') {
505  if (previous != '\\')
506  inPreprocessorLine = false;
507  ++lineno;
508  if (newlines > 0) {
509  code << std::string(newlines, '\n');
510  newlines = 0;
511  previous = '\n';
512  }
513  }
514 
515  continue;
516  }
517 
518  if ((ch == '#') && (str.compare(i, 7, "#error ") == 0 || str.compare(i, 9, "#warning ") == 0)) {
519  if (str.compare(i, 6, "#error") == 0)
520  code << "#error";
521 
522  i = str.find('\n', i);
523  if (i == std::string::npos)
524  break;
525 
526  --i;
527  continue;
528  }
529 
530  // Remove comments..
531  if (str.compare(i, 2, "//") == 0) {
532  const std::size_t commentStart = i + 2;
533  i = str.find('\n', i);
534  if (i == std::string::npos)
535  break;
536  std::string comment(str, commentStart, i - commentStart);
537 
539  std::istringstream iss(comment);
540  std::string word;
541  iss >> word;
542  if (word == "cppcheck-suppress") {
543  iss >> word;
544  if (iss)
545  suppressionIDs.push_back(word);
546  }
547  }
548 
549  if (detectFallThroughComments && isFallThroughComment(comment)) {
550  fallThroughComment = true;
551  }
552 
553  code << "\n";
554  previous = '\n';
555  ++lineno;
556  } else if (str.compare(i, 2, "/*") == 0) {
557  const std::size_t commentStart = i + 2;
558  unsigned char chPrev = 0;
559  ++i;
560  while (i < str.length() && (chPrev != '*' || ch != '/')) {
561  chPrev = ch;
562  ++i;
563  ch = static_cast<unsigned char>(str[i]);
564  if (ch == '\n') {
565  ++newlines;
566  ++lineno;
567  }
568  }
569  std::string comment(str, commentStart, i - commentStart - 1);
570 
571  if (detectFallThroughComments && isFallThroughComment(comment)) {
572  fallThroughComment = true;
573  }
574 
576  std::istringstream iss(comment);
577  std::string word;
578  iss >> word;
579  if (word == "cppcheck-suppress") {
580  iss >> word;
581  if (iss)
582  suppressionIDs.push_back(word);
583  }
584  }
585  } else if ((i == 0 || std::isspace((unsigned char)str[i-1])) && str.compare(i, 5, "__asm") == 0) {
586  while (i < str.size() && (std::isalpha((unsigned char)str[i]) || str[i] == '_'))
587  code << str[i++];
588  while (i < str.size() && std::isspace((unsigned char)str[i])) {
589  if (str[i] == '\n')
590  lineno++;
591  code << str[i++];
592  }
593  if (str[i] == '{') {
594  // Ticket 4873: Extract comments from the __asm / __asm__'s content
595  std::string asmBody;
596  while (i < str.size() && str[i] != '}') {
597  if (str[i] == ';') {
598  std::string::size_type backslashN = str.find('\n', i);
599  if (backslashN != std::string::npos) // Ticket #4922: Don't go in infinite loop or crash if there is no '\n'
600  i = backslashN;
601  }
602  if (str[i] == '\n')
603  lineno++;
604  asmBody += str[i++];
605  }
606  code << removeComments(asmBody, filename);
607  code << '}';
608  } else
609  --i;
610  } else if (ch == '#' && previous == '\n') {
611  code << ch;
612  previous = ch;
613  inPreprocessorLine = true;
614 
615  // Add any pending inline suppressions that have accumulated.
616  if (!suppressionIDs.empty()) {
617  // Add the suppressions.
618  for (std::size_t j = 0; j < suppressionIDs.size(); ++j) {
619  const std::string errmsg(_settings.nomsg.addSuppression(suppressionIDs[j], filename, lineno));
620  if (!errmsg.empty()) {
621  writeError(filename, lineno, _errorLogger, "cppcheckError", errmsg);
622  }
623  }
624  suppressionIDs.clear();
625  }
626  } else {
627  if (!inPreprocessorLine) {
628  // Not whitespace, not a comment, and not preprocessor.
629  // Must be code here!
630 
631  // First check for a "fall through" comment match, but only
632  // add a suppression if the next token is 'case' or 'default'
633  if (detectFallThroughComments && fallThroughComment) {
634  const std::string::size_type j = str.find_first_not_of("abcdefghijklmnopqrstuvwxyz", i);
635  const std::string tok = str.substr(i, j - i);
636  if (tok == "case" || tok == "default")
637  suppressionIDs.push_back("switchCaseFallThrough");
638  fallThroughComment = false;
639  }
640 
641  // Add any pending inline suppressions that have accumulated.
642  if (!suppressionIDs.empty()) {
643  // Relative filename
644  std::string relativeFilename(filename);
646  for (std::size_t j = 0U; j < _settings._basePaths.size(); ++j) {
647  const std::string bp = _settings._basePaths[j] + "/";
648  if (relativeFilename.compare(0,bp.size(),bp)==0) {
649  relativeFilename = relativeFilename.substr(bp.size());
650  }
651  }
652  }
653 
654  // Add the suppressions.
655  for (std::size_t j = 0; j < suppressionIDs.size(); ++j) {
656  const std::string errmsg(_settings.nomsg.addSuppression(suppressionIDs[j], relativeFilename, lineno));
657  if (!errmsg.empty()) {
658  writeError(filename, lineno, _errorLogger, "cppcheckError", errmsg);
659  }
660  }
661  suppressionIDs.clear();
662  }
663  }
664 
665  // String or char constants..
666  if (ch == '\"' || ch == '\'') {
667  code << char(ch);
668  char chNext;
669  do {
670  ++i;
671  chNext = str[i];
672  if (chNext == '\\') {
673  ++i;
674  const char chSeq = str[i];
675  if (chSeq == '\n')
676  ++newlines;
677  else {
678  code << chNext;
679  code << chSeq;
680  previous = static_cast<unsigned char>(chSeq);
681  }
682  } else {
683  code << chNext;
684  previous = static_cast<unsigned char>(chNext);
685  }
686  } while (i < str.length() && chNext != ch && chNext != '\n');
687  }
688 
689  // Rawstring..
690  else if (str.compare(i,2,"R\"")==0) {
691  std::string delim;
692  for (std::string::size_type i2 = i+2; i2 < str.length(); ++i2) {
693  if (i2 > 16 ||
694  std::isspace(str[i2]) ||
695  std::iscntrl(str[i2]) ||
696  str[i2] == ')' ||
697  str[i2] == '\\') {
698  delim = " ";
699  break;
700  } else if (str[i2] == '(')
701  break;
702 
703  delim += str[i2];
704  }
705  const std::string::size_type endpos = str.find(")" + delim + "\"", i);
706  if (delim != " " && endpos != std::string::npos) {
707  unsigned int rawstringnewlines = 0;
708  code << '\"';
709  for (std::string::size_type p = i + 3 + delim.size(); p < endpos; ++p) {
710  if (str[p] == '\n') {
711  rawstringnewlines++;
712  code << "\\n";
713  } else if (std::iscntrl((unsigned char)str[p]) ||
714  std::isspace((unsigned char)str[p])) {
715  code << " ";
716  } else if (str[p] == '\"' || str[p] == '\'') {
717  code << "\\" << (char)str[p];
718  } else {
719  code << (char)str[p];
720  }
721  }
722  code << "\"";
723  if (rawstringnewlines > 0)
724  code << std::string(rawstringnewlines, '\n');
725  i = endpos + delim.size() + 1;
726  } else {
727  code << "R";
728  previous = 'R';
729  }
730  } else {
731  code << char(ch);
732  previous = ch;
733  }
734  }
735  }
736 
737  return code.str();
738 }
739 
740 std::string Preprocessor::removeIf0(const std::string &code)
741 {
742  std::ostringstream ret;
743  std::istringstream istr(code);
744  std::string line;
745  while (std::getline(istr,line)) {
746  ret << line << "\n";
747  if (line == "#if 0") {
748  // goto the end of the '#if 0' block
749  unsigned int level = 1;
750  bool in = false;
751  while (level > 0 && std::getline(istr,line)) {
752  if (line.compare(0,3,"#if") == 0)
753  ++level;
754  else if (line == "#endif")
755  --level;
756  else if ((line == "#else") || (line.compare(0, 5, "#elif") == 0)) {
757  if (level == 1)
758  in = true;
759  } else {
760  if (in)
761  ret << line << "\n";
762  else
763  // replace code within '#if 0' block with empty lines
764  ret << "\n";
765  continue;
766  }
767 
768  ret << line << "\n";
769  }
770  }
771  }
772  return ret.str();
773 }
774 
775 
776 std::string Preprocessor::removeParentheses(const std::string &str)
777 {
778  if (str.find("\n#if") == std::string::npos && str.compare(0, 3, "#if") != 0)
779  return str;
780 
781  std::istringstream istr(str);
782  std::ostringstream ret;
783  std::string line;
784  while (std::getline(istr, line)) {
785  if (line.compare(0, 3, "#if") == 0 || line.compare(0, 5, "#elif") == 0) {
786  std::string::size_type pos;
787  pos = 0;
788  while ((pos = line.find(" (", pos)) != std::string::npos)
789  line.erase(pos, 1);
790  pos = 0;
791  while ((pos = line.find("( ", pos)) != std::string::npos)
792  line.erase(pos + 1, 1);
793  pos = 0;
794  while ((pos = line.find(" )", pos)) != std::string::npos)
795  line.erase(pos, 1);
796  pos = 0;
797  while ((pos = line.find(") ", pos)) != std::string::npos)
798  line.erase(pos + 1, 1);
799 
800  // Remove inner parentheses "((..))"..
801  pos = 0;
802  while ((pos = line.find("((", pos)) != std::string::npos) {
803  ++pos;
804  std::string::size_type pos2 = line.find_first_of("()", pos + 1);
805  if (pos2 != std::string::npos && line[pos2] == ')') {
806  line.erase(pos2, 1);
807  line.erase(pos, 1);
808  }
809  }
810 
811  // "#if(A) => #if A", but avoid "#if (defined A) || defined (B)"
812  if ((line.compare(0, 4, "#if(") == 0 || line.compare(0, 6, "#elif(") == 0) &&
813  line[line.length() - 1] == ')') {
814  int ind = 0;
815  for (std::string::size_type i = 0; i < line.length(); ++i) {
816  if (line[i] == '(')
817  ++ind;
818  else if (line[i] == ')') {
819  --ind;
820  if (ind == 0) {
821  if (i == line.length() - 1) {
822  line[line.find('(')] = ' ';
823  line.erase(line.length() - 1);
824  }
825  break;
826  }
827  }
828  }
829  }
830 
831  if (line.compare(0, 4, "#if(") == 0)
832  line.insert(3, " ");
833  else if (line.compare(0, 6, "#elif(") == 0)
834  line.insert(5, " ");
835  }
836  ret << line << "\n";
837  }
838 
839  return ret.str();
840 }
841 
842 
843 void Preprocessor::removeAsm(std::string &str)
844 {
845  std::string::size_type pos = 0;
846  while ((pos = str.find("#asm\n", pos)) != std::string::npos) {
847  str.replace(pos, 4, "asm(");
848 
849  std::string::size_type pos2 = str.find("#endasm", pos);
850  if (pos2 != std::string::npos) {
851  str.replace(pos2, 7, ");");
852  pos = pos2;
853  }
854  }
855 }
856 
857 
858 void Preprocessor::preprocess(std::istream &istr, std::map<std::string, std::string> &result, const std::string &filename, const std::list<std::string> &includePaths)
859 {
860  std::list<std::string> configs;
861  std::string data;
862  preprocess(istr, data, configs, filename, includePaths);
863  for (std::list<std::string>::const_iterator it = configs.begin(); it != configs.end(); ++it) {
864  if (_settings.userUndefs.find(*it) == _settings.userUndefs.end()) {
865  result[ *it ] = getcode(data, *it, filename);
866  }
867  }
868 }
869 
870 std::string Preprocessor::removeSpaceNearNL(const std::string &str)
871 {
872  std::string tmp;
873  char prev = '\n'; // treat start of file as newline
874  for (std::size_t i = 0; i < str.size(); i++) {
875  if (str[i] == ' ' &&
876  (prev == '\n' ||
877  i + 1 >= str.size() || // treat end of file as newline
878  str[i+1] == '\n'
879  )
880  ) {
881  // Ignore space that has new line in either side of it
882  } else {
883  tmp.append(1, str[i]);
884  prev = str[i];
885  }
886  }
887 
888  return tmp;
889 }
890 
891 void Preprocessor::replaceIfDefined(std::string &str) const
892 {
893  std::string::size_type pos = 0;
894  while ((pos = str.find("#if defined(", pos)) != std::string::npos) {
895  std::string::size_type pos2 = str.find(')', pos + 9);
896  if (pos2 > str.length() - 1)
897  break;
898  if (str[pos2+1] == '\n') {
899  str.erase(pos2, 1);
900  str.erase(pos + 3, 9);
901  str.insert(pos + 3, "def ");
902  }
903  ++pos;
904 
905  if (_settings.terminated())
906  return;
907  }
908 
909  pos = 0;
910  while ((pos = str.find("#if !defined(", pos)) != std::string::npos) {
911  std::string::size_type pos2 = str.find(')', pos + 9);
912  if (pos2 > str.length() - 1)
913  break;
914  if (str[pos2+1] == '\n') {
915  str.erase(pos2, 1);
916  str.erase(pos + 3, 10);
917  str.insert(pos + 3, "ndef ");
918  }
919  ++pos;
920 
921  if (_settings.terminated())
922  return;
923  }
924 
925  pos = 0;
926  while ((pos = str.find("#elif defined(", pos)) != std::string::npos) {
927  std::string::size_type pos2 = str.find(')', pos + 9);
928  if (pos2 > str.length() - 1)
929  break;
930  if (str[pos2+1] == '\n') {
931  str.erase(pos2, 1);
932  str.erase(pos + 6, 8);
933  }
934  ++pos;
935 
936  if (_settings.terminated())
937  return;
938  }
939 }
940 
941 void Preprocessor::preprocessWhitespaces(std::string &processedFile)
942 {
943  // Replace all tabs with spaces..
944  std::replace(processedFile.begin(), processedFile.end(), '\t', ' ');
945 
946  // Remove space characters that are after or before new line character
947  processedFile = removeSpaceNearNL(processedFile);
948 }
949 
950 void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list<std::string> &resultConfigurations, const std::string &filename, const std::list<std::string> &includePaths)
951 {
952  std::string forcedIncludes;
953 
954  if (file0.empty())
955  file0 = filename;
956 
957  processedFile = read(srcCodeStream, filename);
958 
959  for (std::list<std::string>::iterator it = _settings.userIncludes.begin();
960  it != _settings.userIncludes.end();
961  ++it) {
962  const std::string& cur = *it;
963 
964  // try to open file
965  std::ifstream fin;
966 
967  fin.open(cur.c_str());
968  if (!fin.is_open()) {
969  missingInclude(cur,
970  1,
971  cur,
972  UserHeader
973  );
974  continue;
975  }
976  const std::string fileData = read(fin, filename);
977 
978  fin.close();
979 
980  forcedIncludes +=
981  "#file \"" + cur + "\"\n" +
982  "#line 1\n" +
983  fileData + "\n" +
984  "#endfile\n"
985  ;
986  }
987 
988  for (std::vector<std::string>::iterator it = _settings.library.defines.begin();
989  it != _settings.library.defines.end();
990  ++it) {
991  forcedIncludes += *it;
992  }
993 
994  if (!forcedIncludes.empty()) {
995  processedFile =
996  forcedIncludes +
997  "#file \"" + filename + "\"\n" +
998  "#line 1\n" +
999  processedFile +
1000  "#endfile\n"
1001  ;
1002  }
1003 
1004  // Remove asm(...)
1005  removeAsm(processedFile);
1006 
1007  // Replace "defined A" with "defined(A)"
1008  {
1009  std::istringstream istr(processedFile);
1010  std::ostringstream ostr;
1011  std::string line;
1012  while (std::getline(istr, line)) {
1013  if (line.compare(0, 4, "#if ") == 0 || line.compare(0, 6, "#elif ") == 0) {
1014  std::string::size_type pos = 0;
1015  while ((pos = line.find(" defined ")) != std::string::npos) {
1016  line[pos+8] = '(';
1017  pos = line.find_first_of(" |&", pos + 8);
1018  if (pos == std::string::npos)
1019  line += ")";
1020  else
1021  line.insert(pos, ")");
1022 
1023  if (_settings.terminated())
1024  return;
1025  }
1026  }
1027  ostr << line << "\n";
1028  }
1029  processedFile = ostr.str();
1030  }
1031 
1032  std::map<std::string, std::string> defs(getcfgmap(_settings.userDefines, &_settings, filename));
1033 
1034  if (_settings._maxConfigs == 1U) {
1035  std::set<std::string> pragmaOnce;
1036  std::list<std::string> includes;
1037  processedFile = handleIncludes(processedFile, filename, includePaths, defs, pragmaOnce, includes);
1038  resultConfigurations = getcfgs(processedFile, filename, defs);
1039  } else {
1040  handleIncludes(processedFile, filename, includePaths);
1041 
1042  replaceIfDefined(processedFile);
1043 
1044  // Get all possible configurations..
1045  resultConfigurations = getcfgs(processedFile, filename, defs);
1046 
1047  // Remove configurations that are disabled by -U
1048  handleUndef(resultConfigurations);
1049  }
1050 }
1051 
1052 void Preprocessor::handleUndef(std::list<std::string> &configurations) const
1053 {
1054  if (!_settings.userUndefs.empty()) {
1055  for (std::list<std::string>::iterator cfg = configurations.begin(); cfg != configurations.end();) {
1056  bool undef = false;
1057  for (std::set<std::string>::const_iterator it = _settings.userUndefs.begin(); it != _settings.userUndefs.end(); ++it) {
1058  if (*it == *cfg)
1059  undef = true;
1060  else if (cfg->compare(0,it->length(),*it)==0 && cfg->find_first_of(";=") == it->length())
1061  undef = true;
1062  else if (cfg->find(";" + *it) == std::string::npos)
1063  ;
1064  else if (cfg->find(";" + *it + ";") != std::string::npos)
1065  undef = true;
1066  else if (cfg->find(";" + *it + "=") != std::string::npos)
1067  undef = true;
1068  else if (cfg->find(";" + *it) + it->size() + 1U == cfg->size())
1069  undef = true;
1070  }
1071 
1072  if (undef)
1073  configurations.erase(cfg++);
1074  else
1075  ++cfg;
1076  }
1077  }
1078 }
1079 
1080 // Get the DEF in this line: "#ifdef DEF"
1081 std::string Preprocessor::getdef(std::string line, bool def)
1082 {
1083  if (line.empty() || line[0] != '#')
1084  return "";
1085 
1086  // If def is true, the line must start with "#ifdef"
1087  if (def && line.compare(0, 7, "#ifdef ") != 0 && line.compare(0, 4, "#if ") != 0
1088  && (line.compare(0, 6, "#elif ") != 0 || line.compare(0, 7, "#elif !") == 0)) {
1089  return "";
1090  }
1091 
1092  // If def is false, the line must start with "#ifndef"
1093  if (!def && line.compare(0, 8, "#ifndef ") != 0 && line.compare(0, 7, "#elif !") != 0) {
1094  return "";
1095  }
1096 
1097  // Remove the "#ifdef" or "#ifndef"
1098  if (line.compare(0, 12, "#if defined ") == 0)
1099  line.erase(0, 11);
1100  else if (line.compare(0, 15, "#elif !defined(") == 0) {
1101  line.erase(0, 15);
1102  std::string::size_type pos = line.find(')');
1103  // if pos == ::npos then another part of the code will complain
1104  // about the mismatch
1105  if (pos != std::string::npos)
1106  line.erase(pos, 1);
1107  } else
1108  line.erase(0, line.find(' '));
1109 
1110  // Remove all spaces.
1111  std::string::size_type pos = 0;
1112  while ((pos = line.find(' ', pos)) != std::string::npos) {
1113  const unsigned char chprev(static_cast<unsigned char>((pos > 0) ? line[pos-1] : 0));
1114  const unsigned char chnext(static_cast<unsigned char>((pos + 1 < line.length()) ? line[pos+1] : 0));
1115  if ((std::isalnum(chprev) || chprev == '_') && (std::isalnum(chnext) || chnext == '_'))
1116  ++pos;
1117  else
1118  line.erase(pos, 1);
1119  }
1120 
1121  // The remaining string is our result.
1122  return line;
1123 }
1124 
1125 /** Simplify variable in variable map. */
1126 static Token *simplifyVarMapExpandValue(Token *tok, const std::map<std::string, std::string> &variables, std::set<std::string> seenVariables, const Settings& settings)
1127 {
1128  // TODO: handle function-macros too.
1129 
1130  // Prevent infinite recursion..
1131  if (seenVariables.find(tok->str()) != seenVariables.end())
1132  return tok;
1133  seenVariables.insert(tok->str());
1134 
1135  const std::map<std::string, std::string>::const_iterator it = variables.find(tok->str());
1136  if (it != variables.end()) {
1137  TokenList tokenList(&settings);
1138  std::istringstream istr(it->second);
1139  if (tokenList.createTokens(istr)) {
1140  // expand token list
1141  for (Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) {
1142  if (tok2->isName()) {
1143  tok2 = simplifyVarMapExpandValue(tok2, variables, seenVariables, settings);
1144  }
1145  }
1146 
1147  // insert token list into "parent" token list
1148  for (const Token *tok2 = tokenList.front(); tok2; tok2 = tok2->next()) {
1149  if (tok2->previous()) {
1150  tok->insertToken(tok2->str());
1151  tok = tok->next();
1152  } else
1153  tok->str(tok2->str());
1154  }
1155  }
1156  }
1157 
1158  return tok;
1159 }
1160 
1161 /**
1162  * Simplifies the variable map. For example if the map contains A=>B, B=>1, then A=>B is simplified to A=>1.
1163  * @param [in,out] variables - a map of variable name to variable value. This map will be modified.
1164  */
1165 static void simplifyVarMap(std::map<std::string, std::string> &variables, const Settings& settings)
1166 {
1167  for (std::map<std::string, std::string>::iterator i = variables.begin(); i != variables.end(); ++i) {
1168  TokenList tokenList(&settings);
1169  std::istringstream istr(i->second);
1170  if (tokenList.createTokens(istr)) {
1171  for (Token *tok = tokenList.front(); tok; tok = tok->next()) {
1172  if (tok->isName()) {
1173  std::set<std::string> seenVariables;
1174  tok = simplifyVarMapExpandValue(tok, variables, seenVariables, settings);
1175  }
1176  }
1177 
1178  std::string str;
1179  for (const Token *tok = tokenList.front(); tok; tok = tok->next())
1180  str.append((tok->previous() ? " " : "") + tok->str());
1181  i->second = str;
1182  }
1183  }
1184 }
1185 
1186 std::list<std::string> Preprocessor::getcfgs(const std::string &filedata, const std::string &filename, const std::map<std::string, std::string> &defs)
1187 {
1188  std::list<std::string> ret;
1189  ret.push_back("");
1190 
1191  std::list<std::string> deflist, ndeflist;
1192 
1193  // constants defined through "#define" in the code..
1194  std::set<std::string> defines;
1195  std::map<std::string, std::string> alldefinesmap(defs);
1196  std::stack<std::pair<std::string,bool> > includeStack;
1197  includeStack.push(std::pair<std::string,bool>(filename,false));
1198 
1199  // How deep into included files are we currently parsing?
1200  // 0=>Source file, 1=>Included by source file, 2=>included by header that was included by source file, etc
1201  int filelevel = 0;
1202 
1203  bool includeguard = false;
1204  unsigned int linenr = 0;
1205  std::istringstream istr(filedata);
1206  std::string line;
1207  const bool printDebug = _settings.debugwarnings;
1208  while (std::getline(istr, line)) {
1209  ++linenr;
1210 
1211  if (_settings.terminated())
1212  return ret;
1213 
1214  if (_errorLogger)
1215  _errorLogger->reportProgress(filename, "Preprocessing (get configurations 1)", 0);
1216 
1217  if (line.empty())
1218  continue;
1219 
1220  if (line.compare(0, 6, "#file ") == 0) {
1221  includeguard = true;
1222  const std::string::size_type start=line.find('\"');
1223  const std::string::size_type end=line.find('\"',start+1);
1224  const std::string includeFile=line.substr(start+1,end-start-1);
1225  ++filelevel;
1226  bool fileExcluded = _settings.configurationExcluded(includeFile);
1227  includeStack.push(std::pair<std::string,bool>(includeFile,fileExcluded));
1228  continue;
1229  }
1230 
1231  else if (line == "#endfile") {
1232  includeguard = false;
1233  includeStack.pop();
1234  if (filelevel > 0)
1235  --filelevel;
1236  continue;
1237  }
1238 
1239  if (line.compare(0, 8, "#define ") == 0) {
1240  bool valid = false;
1241  for (std::string::size_type pos = 8; pos < line.size(); ++pos) {
1242  const char ch = line[pos];
1243  if (ch=='_' || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z') || (pos>8 && ch>='0' && ch<='9')) {
1244  valid = true;
1245  continue;
1246  }
1247  if (ch==' ' || ch=='(') {
1248  if (valid)
1249  break;
1250  }
1251  valid = false;
1252  break;
1253  }
1254  if (!valid)
1255  line.clear();
1256  else {
1257  std::string definestr = line.substr(8);
1258  const std::string::size_type spacepos = definestr.find(' ');
1259  if (spacepos != std::string::npos)
1260  definestr[spacepos] = '=';
1261  defines.insert(definestr);
1262 
1263  const std::string::size_type separatorpos = definestr.find_first_of("=(");
1264  if (separatorpos != std::string::npos && definestr[separatorpos] == '=') {
1265  const std::string varname(definestr.substr(0, separatorpos));
1266  const std::string value(definestr.substr(separatorpos + 1));
1267  alldefinesmap[varname] = value;
1268  }
1269  }
1270  }
1271 
1272  if (!line.empty() && line.compare(0, 3, "#if") != 0)
1273  includeguard = false;
1274 
1275  if (line.empty() || line[0] != '#')
1276  continue;
1277 
1278  if (includeguard)
1279  continue;
1280 
1281  if (line.compare(0, 5, "#line") == 0)
1282  continue;
1283 
1284  bool from_negation = false;
1285 
1286  std::string def = getdef(line, true);
1287  if (def.empty()) {
1288  def = getdef(line, false);
1289  // sub conditionals of ndef blocks need to be
1290  // constructed _without_ the negated define
1291  if (!def.empty())
1292  from_negation = true;
1293  }
1294  if (!def.empty()) {
1295  int par = 0;
1296  for (std::string::size_type pos = 0; pos < def.length(); ++pos) {
1297  if (def[pos] == '(')
1298  ++par;
1299  else if (def[pos] == ')') {
1300  --par;
1301  if (par < 0)
1302  break;
1303  }
1304  }
1305  if (par != 0) {
1306  std::ostringstream lineStream;
1307  lineStream << __LINE__;
1308  const std::string errorId = "preprocessor" + lineStream.str();
1309  const std::string errorText = "mismatching number of '(' and ')' in this line: " + def;
1310  writeError(filename, linenr, _errorLogger, errorId, errorText);
1311  ret.clear();
1312  return ret;
1313  }
1314 
1315  // Replace defined constants
1316  simplifyCondition(alldefinesmap, def, false);
1317 
1318  if (! deflist.empty() && line.compare(0, 6, "#elif ") == 0)
1319  deflist.pop_back();
1320 
1321  // translate A==1 condition to A=1 configuration
1322  if (def.find("==") != std::string::npos) {
1323  // Check if condition match pattern "%name% == %num%"
1324  // %name%
1325  std::string::size_type pos = 0;
1326  if (std::isalpha((unsigned char)def[pos]) || def[pos] == '_') {
1327  ++pos;
1328  while (std::isalnum((unsigned char)def[pos]) || def[pos] == '_')
1329  ++pos;
1330  }
1331 
1332  // ==
1333  if (def.compare(pos,2,"==")==0)
1334  pos += 2;
1335 
1336  // %num%
1337  if (pos<def.size() && std::isdigit(def[pos])) {
1338  if (def.compare(pos,2,"0x")==0) {
1339  pos += 2;
1340  if (pos >= def.size())
1341  pos = 0;
1342  while (pos < def.size() && std::isxdigit((unsigned char)def[pos]))
1343  ++pos;
1344  } else {
1345  while (pos < def.size() && std::isdigit((unsigned char)def[pos]))
1346  ++pos;
1347  }
1348 
1349  // Does the condition match the pattern "%name% == %num%"?
1350  if (pos == def.size()) {
1351  def.erase(def.find("=="),1);
1352  }
1353  }
1354  }
1355 
1356  deflist.push_back(def);
1357  def = "";
1358 
1359  for (std::list<std::string>::const_iterator it = deflist.begin(); it != deflist.end(); ++it) {
1360  if (*it == "0")
1361  break;
1362  if (*it == "1" || *it == "!")
1363  continue;
1364 
1365  // don't add "T;T":
1366  // treat two and more similar nested conditions as one
1367  if (def != *it) {
1368  if (! def.empty())
1369  def += ";";
1370  def += *it;
1371  }
1372 
1373  /* TODO: Fix TestPreprocessor::test7e (#2552)
1374  else
1375  {
1376  std::ostringstream lineStream;
1377  lineStream << __LINE__;
1378 
1379  ErrorLogger::ErrorMessage errmsg;
1380  ErrorLogger::ErrorMessage::FileLocation loc;
1381  loc.setfile(filename);
1382  loc.line = linenr;
1383  errmsg._callStack.push_back(loc);
1384  errmsg._severity = Severity::error;
1385  errmsg.setmsg(*it+" is already guaranteed to be defined");
1386  errmsg._id = "preprocessor" + lineStream.str();
1387  _errorLogger->reportErr(errmsg);
1388  }
1389  */
1390  }
1391  if (from_negation) {
1392  ndeflist.push_back(deflist.back());
1393  deflist.back() = "!";
1394  }
1395 
1396  if (std::find(ret.begin(), ret.end(), def) == ret.end()) {
1397  if (!includeStack.top().second) {
1398  ret.push_back(def);
1399  } else {
1400  if (_errorLogger && printDebug) {
1401  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
1402  const ErrorLogger::ErrorMessage errmsg(locationList, Severity::debug,
1403  "Configuration not considered: " + def +" for file:"+includeStack.top().first, "debug", false);
1404  _errorLogger->reportErr(errmsg);
1405  }
1406  }
1407  }
1408  }
1409 
1410  else if (line.compare(0, 5, "#else") == 0 && ! deflist.empty()) {
1411  if (deflist.back() == "!" && !ndeflist.empty()) {
1412  deflist.back() = ndeflist.back();
1413  ndeflist.pop_back();
1414  } else {
1415  std::string tempDef((deflist.back() == "1") ? "0" : "1");
1416  deflist.back() = tempDef;
1417  }
1418  }
1419 
1420  else if (line.compare(0, 6, "#endif") == 0 && ! deflist.empty()) {
1421  if (deflist.back() == "!" && !ndeflist.empty())
1422  ndeflist.pop_back();
1423  deflist.pop_back();
1424  }
1425  }
1426 
1427  // Remove defined constants from ifdef configurations..
1428  std::size_t count = 0;
1429  for (std::list<std::string>::iterator it = ret.begin(); it != ret.end(); ++it) {
1430  if (_errorLogger)
1431  _errorLogger->reportProgress(filename, "Preprocessing (get configurations 2)", (100 * count++) / ret.size());
1432 
1433  std::string cfg(*it);
1434  for (std::set<std::string>::const_iterator it2 = defines.begin(); it2 != defines.end(); ++it2) {
1435  std::string::size_type pos = 0;
1436 
1437  // Get name of define
1438  std::string defineName(*it2);
1439  if (defineName.find_first_of("=(") != std::string::npos)
1440  defineName.erase(defineName.find_first_of("=("));
1441 
1442  // Remove ifdef configurations that match the defineName
1443  while ((pos = cfg.find(defineName, pos)) != std::string::npos) {
1444  const std::string::size_type pos1 = pos;
1445  ++pos;
1446  if (pos1 > 0 && cfg[pos1-1] != ';')
1447  continue;
1448  const std::string::size_type pos2 = pos1 + defineName.length();
1449  if (pos2 < cfg.length() && cfg[pos2] != ';')
1450  continue;
1451  --pos;
1452  cfg.erase(pos, defineName.length());
1453  }
1454  }
1455  if (cfg.length() != it->length()) {
1456  while (cfg.length() > 0 && cfg[0] == ';')
1457  cfg.erase(0, 1);
1458 
1459  while (cfg.length() > 0 && cfg.back() == ';')
1460  cfg.erase(cfg.length() - 1);
1461 
1462  std::string::size_type pos = 0;
1463  while ((pos = cfg.find(";;", pos)) != std::string::npos)
1464  cfg.erase(pos, 1);
1465 
1466  *it = cfg;
1467  }
1468  }
1469 
1470  // convert configurations: "defined(A) && defined(B)" => "A;B"
1471  for (std::list<std::string>::iterator it = ret.begin(); it != ret.end(); ++it) {
1472  std::string s(*it);
1473 
1474  if (s.find("&&") != std::string::npos) {
1475  Tokenizer tokenizer(&_settings, _errorLogger);
1476  if (!tokenizer.tokenizeCondition(s)) {
1477  std::ostringstream lineStream;
1478  lineStream << __LINE__;
1479 
1482  loc.setfile(filename);
1483  loc.line = 1;
1484  errmsg._callStack.push_back(loc);
1485  errmsg._severity = Severity::error;
1486  errmsg.setmsg("Error parsing this: " + s);
1487  errmsg._id = "preprocessor" + lineStream.str();
1488  _errorLogger->reportErr(errmsg);
1489  }
1490 
1491 
1492  const Token *tok = tokenizer.tokens();
1493  std::set<std::string> varList;
1494  while (tok) {
1495  if (Token::Match(tok, "defined ( %name% )")) {
1496  varList.insert(tok->strAt(2));
1497  tok = tok->tokAt(4);
1498  if (tok && tok->str() == "&&") {
1499  tok = tok->next();
1500  }
1501  } else if (Token::Match(tok, "%name% ;")) {
1502  varList.insert(tok->str());
1503  tok = tok->tokAt(2);
1504  } else {
1505  break;
1506  }
1507  }
1508 
1509  s = join(varList, ';');
1510 
1511  if (!s.empty())
1512  *it = s;
1513  }
1514  }
1515 
1516  // Convert configurations into a canonical form: B;C;A or C;A;B => A;B;C
1517  for (std::list<std::string>::iterator it = ret.begin(); it != ret.end(); ++it)
1518  *it = unify(*it, ';');
1519 
1520  // Remove duplicates from the ret list..
1521  ret.sort();
1522  ret.unique();
1523 
1524  // cleanup unhandled configurations..
1525  for (std::list<std::string>::iterator it = ret.begin(); it != ret.end();) {
1526  const std::string s(*it + ";");
1527 
1528  bool unhandled = false;
1529 
1530  for (std::string::size_type pos = 0; pos < s.length(); ++pos) {
1531  const unsigned char c = static_cast<unsigned char>(s[pos]);
1532 
1533  // ok with ";"
1534  if (c == ';')
1535  continue;
1536 
1537  // identifier..
1538  if (std::isalpha(c) || c == '_') {
1539  while (std::isalnum((unsigned char)s[pos]) || s[pos] == '_')
1540  ++pos;
1541  if (s[pos] == '=') {
1542  ++pos;
1543  while (std::isdigit((unsigned char)s[pos]))
1544  ++pos;
1545  if (s[pos] != ';') {
1546  unhandled = true;
1547  break;
1548  }
1549  }
1550 
1551  --pos;
1552  continue;
1553  }
1554 
1555  // not ok..
1556  else {
1557  unhandled = true;
1558  break;
1559  }
1560  }
1561 
1562  if (unhandled) {
1563  // unhandled ifdef configuration..
1564  if (_errorLogger && printDebug) {
1565  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
1566  const ErrorLogger::ErrorMessage errmsg(locationList, Severity::debug, "unhandled configuration: " + *it, "debug", false);
1567  _errorLogger->reportErr(errmsg);
1568  }
1569 
1570  ret.erase(it++);
1571  } else {
1572  ++it;
1573  }
1574  }
1575 
1576  return ret;
1577 }
1578 
1579 
1580 void Preprocessor::simplifyCondition(const std::map<std::string, std::string> &cfg, std::string &condition, bool match)
1581 {
1582  Tokenizer tokenizer(&_settings, _errorLogger);
1583  if (!tokenizer.tokenizeCondition("(" + condition + ")")) {
1584  // If tokenize returns false, then there is syntax error in the
1585  // code which we can't handle. So stop here.
1586  return;
1587  }
1588 
1589  if (Token::Match(tokenizer.tokens(), "( %name% )")) {
1590  std::map<std::string,std::string>::const_iterator var = cfg.find(tokenizer.tokens()->strAt(1));
1591  if (var != cfg.end()) {
1592  const std::string &value = (*var).second;
1593  condition = (value == "0") ? "0" : "1";
1594  } else if (match)
1595  condition = "0";
1596  return;
1597  }
1598 
1599  if (Token::Match(tokenizer.tokens(), "( ! %name% )")) {
1600  std::map<std::string,std::string>::const_iterator var = cfg.find(tokenizer.tokens()->strAt(2));
1601 
1602  if (var == cfg.end())
1603  condition = "1";
1604  else if (var->second == "0")
1605  condition = "1";
1606  else if (match)
1607  condition = "0";
1608  return;
1609  }
1610 
1611  // replace variable names with values..
1612  for (Token *tok = const_cast<Token *>(tokenizer.tokens()); tok; tok = tok->next()) {
1613  if (!tok->isName())
1614  continue;
1615 
1616  if (Token::Match(tok, "defined ( %name% )")) {
1617  if (cfg.find(tok->strAt(2)) != cfg.end())
1618  tok->str("1");
1619  else if (match)
1620  tok->str("0");
1621  else
1622  continue;
1623  tok->deleteNext(3);
1624  continue;
1625  }
1626 
1627  if (Token::Match(tok, "defined %name%")) {
1628  if (cfg.find(tok->strAt(1)) != cfg.end())
1629  tok->str("1");
1630  else if (match)
1631  tok->str("0");
1632  else
1633  continue;
1634  tok->deleteNext();
1635  continue;
1636  }
1637 
1638  const std::map<std::string, std::string>::const_iterator it = cfg.find(tok->str());
1639  if (it != cfg.end()) {
1640  if (!it->second.empty()) {
1641  // Tokenize the value
1642  Tokenizer tokenizer2(&_settings, _errorLogger);
1643  tokenizer2.tokenizeCondition(it->second);
1644 
1645  // Copy the value tokens
1646  std::stack<Token *> link;
1647  for (const Token *tok2 = tokenizer2.tokens(); tok2; tok2 = tok2->next()) {
1648  tok->str(tok2->str());
1649 
1650  if (Token::Match(tok2,"[{([]"))
1651  link.push(tok);
1652  else if (!link.empty() && Token::Match(tok2,"[})]]")) {
1653  Token::createMutualLinks(link.top(), tok);
1654  link.pop();
1655  }
1656 
1657  if (tok2->next()) {
1658  tok->insertToken("");
1659  tok = tok->next();
1660  }
1661  }
1662  } else if ((!tok->previous() || Token::Match(tok->previous(), "&&|%oror%|(")) &&
1663  (!tok->next() || Token::Match(tok->next(), "&&|%oror%|)")))
1664  tok->str("1");
1665  else
1666  tok->deleteThis();
1667  }
1668  }
1669 
1670  // simplify calculations..
1672  bool modified = true;
1673  while (modified) {
1674  modified = false;
1675  modified |= tokenizer.simplifySizeof();
1676  modified |= tokenizer.simplifyCalculations();
1677  modified |= tokenizer.simplifyConstTernaryOp();
1678  modified |= tokenizer.simplifyRedundantParentheses();
1679  for (Token *tok = const_cast<Token *>(tokenizer.tokens()); tok; tok = tok->next()) {
1680  if (Token::Match(tok, "! %num%")) {
1681  tok->deleteThis();
1682  tok->str(tok->str() == "0" ? "1" : "0");
1683  modified = true;
1684  }
1685  }
1686  }
1687 
1688  for (Token *tok = const_cast<Token *>(tokenizer.tokens()); tok; tok = tok->next()) {
1689  if (Token::Match(tok, "(|%oror%|&& %num% &&|%oror%|)")) {
1690  if (tok->next()->str() != "0") {
1691  tok->next()->str("1");
1692  }
1693  }
1694  }
1695 
1696  for (Token *tok = const_cast<Token *>(tokenizer.tokens()); tok; tok = tok->next()) {
1697  while (Token::Match(tok, "(|%oror% %any% %oror% 1")) {
1698  tok->deleteNext(2);
1699  if (tok->tokAt(-3))
1700  tok = tok->tokAt(-3);
1701  }
1702  }
1703 
1704  if (Token::simpleMatch(tokenizer.tokens(), "( 1 )") ||
1705  Token::simpleMatch(tokenizer.tokens(), "( 1 ||"))
1706  condition = "1";
1707  else if (Token::simpleMatch(tokenizer.tokens(), "( 0 )"))
1708  condition = "0";
1709 }
1710 
1711 bool Preprocessor::match_cfg_def(std::map<std::string, std::string> cfg, std::string def)
1712 {
1713  /*
1714  std::cout << "cfg: \"";
1715  for (std::map<std::string, std::string>::const_iterator it = cfg.begin(); it != cfg.end(); ++it)
1716  {
1717  std::cout << it->first;
1718  if (!it->second.empty())
1719  std::cout << "=" << it->second;
1720  std::cout << ";";
1721  }
1722  std::cout << "\" ";
1723  std::cout << "def: \"" << def << "\"\n";
1724  */
1725 
1726  simplifyVarMap(cfg, _settings);
1727  simplifyCondition(cfg, def, true);
1728 
1729  if (cfg.find(def) != cfg.end())
1730  return true;
1731 
1732  if (def == "0")
1733  return false;
1734 
1735  if (def == "1")
1736  return true;
1737 
1738  return false;
1739 }
1740 
1741 std::string Preprocessor::getcode(const std::string &filedata, const std::string &cfg, const std::string &filename)
1742 {
1743  // For the error report
1744  unsigned int lineno = 0;
1745 
1746  std::ostringstream ret;
1747 
1748  bool match = true;
1749  std::list<bool> matching_ifdef;
1750  std::list<bool> matched_ifdef;
1751 
1752  // Create a map for the cfg for faster access to defines
1753  std::map<std::string, std::string> cfgmap(getcfgmap(cfg, &_settings, filename));
1754 
1755  std::stack<std::string> filenames;
1756  filenames.push(filename);
1757  std::stack<unsigned int> lineNumbers;
1758  std::istringstream istr(filedata);
1759  std::string line;
1760  while (std::getline(istr, line)) {
1761  ++lineno;
1762 
1763  if (_settings.terminated())
1764  return "";
1765 
1766  if (line.compare(0, 11, "#pragma asm") == 0) {
1767  ret << "\n";
1768  bool found_end = false;
1769  while (getline(istr, line)) {
1770  if (line.compare(0, 14, "#pragma endasm") == 0) {
1771  found_end = true;
1772  break;
1773  }
1774 
1775  ret << "\n";
1776  }
1777  if (!found_end)
1778  break;
1779 
1780  if (line.find('=') != std::string::npos) {
1781  Tokenizer tokenizer(&_settings, _errorLogger);
1782  line.erase(0, sizeof("#pragma endasm"));
1783  std::istringstream tempIstr(line);
1784  tokenizer.tokenize(tempIstr, "", "", true);
1785  if (Token::Match(tokenizer.tokens(), "( %name% = %any% )")) {
1786  ret << "asm(" << tokenizer.tokens()->strAt(1) << ");";
1787  }
1788  }
1789 
1790  ret << "\n";
1791 
1792  continue;
1793  }
1794 
1795  const std::string def = getdef(line, true);
1796  const std::string ndef = getdef(line, false);
1797 
1798  const bool emptymatch = matching_ifdef.empty() || matched_ifdef.empty();
1799 
1800  if (line.compare(0, 8, "#define ") == 0) {
1801  match = true;
1802 
1803 
1804  typedef std::set<std::string>::const_iterator It;
1805  for (It it = _settings.userUndefs.begin(); it != _settings.userUndefs.end(); ++it) {
1806  std::string::size_type pos = line.find_first_not_of(' ',8);
1807  if (pos != std::string::npos) {
1808  std::string::size_type pos2 = line.find(*it,pos);
1809  if ((pos2 != std::string::npos) &&
1810  ((line.size() == pos2 + (*it).size()) ||
1811  (line[pos2 + (*it).size()] == ' ') ||
1812  (line[pos2 + (*it).size()] == '('))) {
1813  match = false;
1814  break;
1815  }
1816  }
1817  }
1818 
1819  if (match) {
1820  for (std::list<bool>::const_iterator it = matching_ifdef.begin(); it != matching_ifdef.end(); ++it) {
1821  if (!bool(*it)) {
1822  match = false;
1823  break;
1824  }
1825  }
1826  }
1827 
1828  if (match) {
1829  const std::string::size_type pos = line.find_first_of(" (", 8);
1830  if (pos == std::string::npos)
1831  cfgmap[line.substr(8)] = "";
1832  else if (line[pos] == ' ') {
1833  std::string value(line.substr(pos + 1));
1834  if (cfgmap.find(value) != cfgmap.end())
1835  value = cfgmap[value];
1836  cfgmap[line.substr(8, pos - 8)] = value;
1837  } else
1838  cfgmap[line.substr(8, pos - 8)] = "";
1839  }
1840  }
1841 
1842  else if (line.compare(0, 7, "#undef ") == 0) {
1843  const std::string name(line.substr(7));
1844  cfgmap.erase(name);
1845  }
1846 
1847  else if (!emptymatch && line.compare(0, 7, "#elif !") == 0) {
1848  if (matched_ifdef.back()) {
1849  matching_ifdef.back() = false;
1850  } else {
1851  if (!match_cfg_def(cfgmap, ndef)) {
1852  matching_ifdef.back() = true;
1853  matched_ifdef.back() = true;
1854  }
1855  }
1856  }
1857 
1858  else if (!emptymatch && line.compare(0, 6, "#elif ") == 0) {
1859  if (matched_ifdef.back()) {
1860  matching_ifdef.back() = false;
1861  } else {
1862  if (match_cfg_def(cfgmap, def)) {
1863  matching_ifdef.back() = true;
1864  matched_ifdef.back() = true;
1865  }
1866  }
1867  }
1868 
1869  else if (line.compare(0,4,"#if ") == 0) {
1870  matching_ifdef.push_back(match_cfg_def(cfgmap, line.substr(4)));
1871  matched_ifdef.push_back(matching_ifdef.back());
1872  }
1873 
1874  else if (! def.empty()) {
1875  matching_ifdef.push_back(cfgmap.find(def) != cfgmap.end());
1876  matched_ifdef.push_back(matching_ifdef.back());
1877  }
1878 
1879  else if (! ndef.empty()) {
1880  matching_ifdef.push_back(cfgmap.find(ndef) == cfgmap.end());
1881  matched_ifdef.push_back(matching_ifdef.back());
1882  }
1883 
1884  else if (!emptymatch && line == "#else") {
1885  if (! matched_ifdef.empty())
1886  matching_ifdef.back() = ! matched_ifdef.back();
1887  }
1888 
1889  else if (line.compare(0, 6, "#endif") == 0) {
1890  if (! matched_ifdef.empty())
1891  matched_ifdef.pop_back();
1892  if (! matching_ifdef.empty())
1893  matching_ifdef.pop_back();
1894  }
1895 
1896  if (!line.empty() && line[0] == '#') {
1897  match = true;
1898  for (std::list<bool>::const_iterator it = matching_ifdef.begin(); it != matching_ifdef.end(); ++it) {
1899  if (!bool(*it)) {
1900  match = false;
1901  break;
1902  }
1903  }
1904  }
1905 
1906  // #error => return ""
1907  if (match && line.compare(0, 6, "#error") == 0) {
1908  if (!_settings.userDefines.empty() && !_settings._force) {
1909  error(filenames.top(), lineno, line);
1910  }
1911  return "";
1912  }
1913 
1914  if (!match && (line.compare(0, 8, "#define ") == 0 ||
1915  line.compare(0, 6, "#undef") == 0)) {
1916  // Remove define that is not part of this configuration
1917  line = "";
1918  } else if (line.compare(0, 7, "#file \"") == 0 ||
1919  line.compare(0, 8, "#endfile") == 0 ||
1920  line.compare(0, 8, "#define ") == 0 ||
1921  line.compare(0, 6, "#line ") == 0 ||
1922  line.compare(0, 6, "#undef") == 0) {
1923  // We must not remove #file tags or line numbers
1924  // are corrupted. File tags are removed by the tokenizer.
1925 
1926  // Keep location info updated
1927  if (line.compare(0, 7, "#file \"") == 0) {
1928  filenames.push(line.substr(7, line.size() - 8));
1929  lineNumbers.push(lineno);
1930  lineno = 0;
1931  } else if (line.compare(0, 8, "#endfile") == 0) {
1932  if (filenames.size() > 1U)
1933  filenames.pop();
1934 
1935  if (!lineNumbers.empty()) {
1936  lineno = lineNumbers.top();
1937  lineNumbers.pop();
1938  }
1939  }
1940  } else if (!match || line.compare(0, 1, "#") == 0) {
1941  // Remove #if, #else, #pragma etc, leaving only
1942  // #define, #undef, #file and #endfile. and also lines
1943  // which are not part of this configuration.
1944  line = "";
1945  }
1946 
1947  ret << line << "\n";
1948  }
1949 
1950  if (!validateCfg(ret.str(), cfg)) {
1951  return "";
1952  }
1953 
1954  return expandMacros(ret.str(), filename, cfg, _errorLogger);
1955 }
1956 
1957 void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg)
1958 {
1959  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
1960  if (!filename.empty()) {
1962  loc.line = linenr;
1963  loc.setfile(filename);
1964  locationList.push_back(loc);
1965  }
1968  msg,
1969  "preprocessorErrorDirective",
1970  false));
1971 }
1972 
1974 {
1975  std::string::size_type i = str.find_first_of("<\"");
1976  if (i == std::string::npos) {
1977  str = "";
1978  return NoHeader;
1979  }
1980 
1981  char c = str[i];
1982  if (c == '<')
1983  c = '>';
1984 
1985  std::string result;
1986  for (i = i + 1; i < str.length(); ++i) {
1987  if (str[i] == c)
1988  break;
1989 
1990  result.append(1, str[i]);
1991  }
1992 
1993  // Linux can't open include paths with \ separator, so fix them
1994  std::replace(result.begin(), result.end(), '\\', '/');
1995 
1996  str = result;
1997 
1998  return (c == '\"') ? UserHeader : SystemHeader;
1999 }
2000 
2001 /**
2002  * Try to open header
2003  * @param filename header name (in/out)
2004  * @param includePaths paths where to look for the file
2005  * @param filePath path to the header file
2006  * @param fin file input stream (in/out)
2007  * @return if file is opened then true is returned
2008  */
2009 static bool openHeader(std::string &filename, const std::list<std::string> &includePaths, const std::string &filePath, std::ifstream &fin)
2010 {
2011  fin.open((filePath + filename).c_str());
2012  if (fin.is_open()) {
2013  filename = filePath + filename;
2014  return true;
2015  }
2016 
2017  std::list<std::string> includePaths2(includePaths);
2018  includePaths2.push_front("");
2019 
2020  for (std::list<std::string>::const_iterator iter = includePaths2.begin(); iter != includePaths2.end(); ++iter) {
2021  const std::string nativePath(Path::toNativeSeparators(*iter));
2022  fin.open((nativePath + filename).c_str());
2023  if (fin.is_open()) {
2024  filename = nativePath + filename;
2025  return true;
2026  }
2027  fin.clear();
2028  }
2029 
2030  return false;
2031 }
2032 
2033 
2034 std::string Preprocessor::handleIncludes(const std::string &code, const std::string &filePath, const std::list<std::string> &includePaths, std::map<std::string,std::string> &defs, std::set<std::string> &pragmaOnce, std::list<std::string> includes)
2035 {
2036  std::string path;
2037  std::string::size_type sep_pos = filePath.find_last_of("\\/");
2038  if (sep_pos != std::string::npos)
2039  path = filePath.substr(0, 1 + sep_pos);
2040 
2041  // current #if indent level.
2042  std::stack<bool>::size_type indent = 0;
2043 
2044  // how deep does the #if match? this can never be bigger than "indent".
2045  std::stack<bool>::size_type indentmatch = 0;
2046 
2047  // has there been a true #if condition at the current indentmatch level?
2048  // then no more #elif or #else can be true before the #endif is seen.
2049  std::stack<bool> elseIsTrueStack;
2050 
2051  unsigned int linenr = 0;
2052 
2053  const std::set<std::string> &undefs = _settings.userUndefs;
2054 
2055  if (_errorLogger)
2056  _errorLogger->reportProgress(filePath, "Preprocessor (handleIncludes)", 0);
2057 
2058  std::ostringstream ostr;
2059  std::istringstream istr(code);
2060  std::string line;
2061  bool suppressCurrentCodePath = false;
2062  while (std::getline(istr,line)) {
2063  ++linenr;
2064 
2065  if (_settings.terminated())
2066  return "";
2067 
2068  // has there been a true #if condition at the current indentmatch level?
2069  // then no more #elif or #else can be true before the #endif is seen.
2070  while (elseIsTrueStack.size() != indentmatch + 1) {
2071  if (elseIsTrueStack.size() < indentmatch + 1) {
2072  elseIsTrueStack.push(true);
2073  } else {
2074  elseIsTrueStack.pop();
2075  }
2076  }
2077 
2078  if (elseIsTrueStack.empty()) {
2079  writeError(filePath, linenr, _errorLogger, "syntaxError", "Syntax error in preprocessor code");
2080  return "";
2081  }
2082 
2083  std::stack<bool>::reference elseIsTrue = elseIsTrueStack.top();
2084 
2085  if (line == "#pragma once") {
2086  pragmaOnce.insert(filePath);
2087  } else if (line.compare(0,7,"#ifdef ") == 0) {
2088  if (indent == indentmatch) {
2089  const std::string tag = getdef(line,true);
2090  if (defs.find(tag) != defs.end()) {
2091  elseIsTrue = false;
2092  indentmatch++;
2093  } else if (undefs.find(tag) != undefs.end()) {
2094  elseIsTrue = true;
2095  indentmatch++;
2096  suppressCurrentCodePath = true;
2097  }
2098  }
2099  ++indent;
2100 
2101  if (indent == indentmatch + 1)
2102  elseIsTrue = true;
2103  } else if (line.compare(0,8,"#ifndef ") == 0) {
2104  if (indent == indentmatch) {
2105  const std::string tag = getdef(line,false);
2106  if (defs.find(tag) == defs.end()) {
2107  elseIsTrue = false;
2108  indentmatch++;
2109  } else if (undefs.find(tag) != undefs.end()) {
2110  elseIsTrue = false;
2111  indentmatch++;
2112  suppressCurrentCodePath = false;
2113  }
2114  }
2115  ++indent;
2116 
2117  if (indent == indentmatch + 1)
2118  elseIsTrue = true;
2119 
2120  } else if (line.compare(0,4,"#if ") == 0) {
2121  if (!suppressCurrentCodePath && indent == indentmatch && match_cfg_def(defs, line.substr(4))) {
2122  elseIsTrue = false;
2123  indentmatch++;
2124  }
2125  ++indent;
2126 
2127  if (indent == indentmatch + 1)
2128  elseIsTrue = true; // this value doesn't matter when suppressCurrentCodePath is true
2129  } else if (line.compare(0,6,"#elif ") == 0 || line.compare(0,5,"#else") == 0) {
2130  if (!elseIsTrue) {
2131  if ((indentmatch > 0) && (indentmatch == indent)) {
2132  indentmatch = indent - 1;
2133  }
2134  } else {
2135  if ((indentmatch > 0) && (indentmatch == indent)) {
2136  indentmatch = indent - 1;
2137  } else if ((indent > 0) && indentmatch == indent - 1) {
2138  if (line.compare(0,5,"#else")==0 || match_cfg_def(defs,line.substr(6))) {
2139  indentmatch = indent;
2140  elseIsTrue = false;
2141  }
2142  }
2143  }
2144  } else if (line.compare(0, 6, "#endif") == 0) {
2145  if (indent > 0)
2146  --indent;
2147  if (indentmatch > indent || indent == 0) {
2148  indentmatch = indent;
2149  elseIsTrue = false;
2150  suppressCurrentCodePath = false;
2151  }
2152  } else if (indentmatch == indent) {
2153  if (!suppressCurrentCodePath && line.compare(0, 8, "#define ") == 0) {
2154  const unsigned int endOfDefine = 8;
2155  std::string::size_type endOfTag = line.find_first_of("( ", endOfDefine);
2156  std::string tag;
2157 
2158  // define a symbol
2159  if (endOfTag == std::string::npos) {
2160  tag = line.substr(endOfDefine);
2161  defs[tag] = "";
2162  } else {
2163  tag = line.substr(endOfDefine, endOfTag-endOfDefine);
2164 
2165  // define a function-macro
2166  if (line[endOfTag] == '(') {
2167  defs[tag] = "";
2168  }
2169  // define value
2170  else {
2171  ++endOfTag;
2172 
2173  const std::string& value = line.substr(endOfTag, line.size()-endOfTag);
2174 
2175  if (defs.find(value) != defs.end())
2176  defs[tag] = defs[value];
2177  else
2178  defs[tag] = value;
2179  }
2180  }
2181 
2182  if (undefs.find(tag) != undefs.end()) {
2183  defs.erase(tag);
2184  }
2185  }
2186 
2187  else if (!suppressCurrentCodePath && line.compare(0,7,"#undef ") == 0) {
2188  defs.erase(line.substr(7));
2189  }
2190 
2191  else if (!suppressCurrentCodePath && line.compare(0,9,"#include ")==0) {
2192  std::string filename(line.substr(9));
2193 
2194  const HeaderTypes headerType = getHeaderFileName(filename);
2195  if (headerType == NoHeader) {
2196  ostr << std::endl;
2197  continue;
2198  }
2199 
2200  // try to open file
2201  std::string filepath;
2202  if (headerType == UserHeader)
2203  filepath = path;
2204  std::ifstream fin;
2205  if (!openHeader(filename, includePaths, filepath, fin)) {
2207  linenr,
2208  filename,
2209  headerType
2210  );
2211  ostr << std::endl;
2212  continue;
2213  }
2214 
2215  // Prevent that files are recursively included
2216  if (std::find(includes.begin(), includes.end(), filename) != includes.end()) {
2217  ostr << std::endl;
2218  continue;
2219  }
2220 
2221  includes.push_back(filename);
2222 
2223  // Don't include header if it's already included and contains #pragma once
2224  if (pragmaOnce.find(filename) != pragmaOnce.end()) {
2225  ostr << std::endl;
2226  continue;
2227  }
2228 
2229  ostr << "#file \"" << filename << "\"\n"
2230  << handleIncludes(read(fin, filename), filename, includePaths, defs, pragmaOnce, includes) << std::endl
2231  << "#endfile\n";
2232  continue;
2233  }
2234 
2235  if (!suppressCurrentCodePath)
2236  ostr << line;
2237  }
2238 
2239  // A line has been read..
2240  ostr << "\n";
2241  }
2242 
2243  return ostr.str();
2244 }
2245 
2246 
2247 void Preprocessor::handleIncludes(std::string &code, const std::string &filePath, const std::list<std::string> &includePaths)
2248 {
2249  std::list<std::string> paths;
2250  std::string path = filePath;
2251  const std::string::size_type sep_pos = path.find_last_of("\\/");
2252  if (sep_pos != std::string::npos)
2253  path.erase(1 + sep_pos);
2254  paths.push_back(path);
2255  std::string::size_type pos = 0;
2256  std::string::size_type endfilePos = 0;
2257  if (code.compare(0,7U,"#file \"")==0) {
2258  const std::string::size_type start = code.find("#file \"" + filePath, 7U);
2259  if (start != std::string::npos)
2260  endfilePos = start;
2261  }
2262  std::set<std::string> handledFiles;
2263  while ((pos = code.find("#include", pos)) != std::string::npos) {
2264  if (_settings.terminated())
2265  return;
2266 
2267  // Accept only includes that are at the start of a line
2268  if (pos > 0 && code[pos-1] != '\n') {
2269  pos += 8; // length of "#include"
2270  continue;
2271  }
2272 
2273  // If endfile is encountered, we have moved to a next file in our stack,
2274  // so remove last path in our list.
2275  while (!paths.empty() && (endfilePos = code.find("\n#endfile", endfilePos)) != std::string::npos && endfilePos < pos) {
2276  paths.pop_back();
2277  endfilePos += 9; // size of #endfile
2278  }
2279 
2280  endfilePos = pos;
2281  std::string::size_type end = code.find('\n', pos);
2282  std::string filename = code.substr(pos, end - pos);
2283 
2284  // Remove #include clause
2285  code.erase(pos, end - pos);
2286 
2287  HeaderTypes headerType = getHeaderFileName(filename);
2288  if (headerType == NoHeader)
2289  continue;
2290 
2291  // filename contains now a file name e.g. "menu.h"
2292  std::string processedFile;
2293  std::string filepath;
2294  if (headerType == UserHeader && !paths.empty())
2295  filepath = paths.back();
2296  std::ifstream fin;
2297  const bool fileOpened(openHeader(filename, includePaths, filepath, fin));
2298 
2299  if (fileOpened) {
2300  filename = Path::simplifyPath(filename);
2301  std::string tempFile = filename;
2302  std::transform(tempFile.begin(), tempFile.end(), tempFile.begin(), tolowerWrapper);
2303  if (handledFiles.find(tempFile) != handledFiles.end()) {
2304  // We have processed this file already once, skip
2305  // it this time to avoid eternal loop.
2306  fin.close();
2307  continue;
2308  }
2309 
2310  handledFiles.insert(tempFile);
2311  processedFile = Preprocessor::read(fin, filename);
2312  fin.close();
2313  }
2314 
2315  if (!processedFile.empty()) {
2316  // Remove space characters that are after or before new line character
2317  processedFile = "#file \"" + Path::fromNativeSeparators(filename) + "\"\n" + processedFile + "\n#endfile";
2318  code.insert(pos, processedFile);
2319 
2320  path = filename;
2321  path.erase(1 + path.find_last_of("\\/"));
2322  paths.push_back(path);
2323  } else if (!fileOpened) {
2324  std::string f = filePath;
2325 
2326  // Determine line number of include
2327  unsigned int linenr = 1;
2328  unsigned int level = 0;
2329  for (std::string::size_type p = 1; p <= pos; ++p) {
2330  if (level == 0 && code[pos-p] == '\n')
2331  ++linenr;
2332  else if (code.compare(pos-p, 9, "#endfile\n") == 0) {
2333  ++level;
2334  } else if (code.compare(pos-p, 6, "#file ") == 0) {
2335  if (level == 0) {
2336  linenr--;
2337  const std::string::size_type pos1 = pos - p + 7;
2338  const std::string::size_type pos2 = code.find_first_of("\"\n", pos1);
2339  f = code.substr(pos1, (pos2 == std::string::npos) ? pos2 : (pos2 - pos1));
2340  break;
2341  }
2342  --level;
2343  }
2344  }
2345 
2347  linenr,
2348  filename,
2349  headerType);
2350  }
2351  }
2352 }
2353 
2354 // Report that include is missing
2355 void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
2356 {
2357  const std::string fname = Path::fromNativeSeparators(filename);
2358  if (_settings.nomsg.isSuppressed("missingInclude", fname, linenr))
2359  return;
2360  if (headerType == SystemHeader && _settings.nomsg.isSuppressed("missingIncludeSystem", fname, linenr))
2361  return;
2362 
2363  if (headerType == SystemHeader)
2364  missingSystemIncludeFlag = true;
2365  else
2366  missingIncludeFlag = true;
2368 
2369  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
2370  if (!filename.empty()) {
2372  loc.line = linenr;
2373  loc.setfile(Path::toNativeSeparators(filename));
2374  locationList.push_back(loc);
2375  }
2376  ErrorLogger::ErrorMessage errmsg(locationList, Severity::information,
2377  (headerType==SystemHeader) ?
2378  "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." :
2379  "Include file: \"" + header + "\" not found.",
2380  (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude",
2381  false);
2382  errmsg.file0 = file0;
2383  _errorLogger->reportInfo(errmsg);
2384  }
2385 }
2386 
2387 /**
2388  * Skip string in line. A string begins and ends with either a &quot; or a &apos;
2389  * @param line the string
2390  * @param pos in=start position of string, out=end position of string
2391  */
2392 static void skipstring(const std::string &line, std::string::size_type &pos)
2393 {
2394  const char ch = line[pos];
2395 
2396  ++pos;
2397  while (pos < line.size() && line[pos] != ch) {
2398  if (line[pos] == '\\')
2399  ++pos;
2400  ++pos;
2401  }
2402 }
2403 
2404 /**
2405  * Remove heading and trailing whitespaces from the input parameter.
2406  * @param s The string to trim.
2407  */
2408 static std::string trim(const std::string& s)
2409 {
2410  const std::string::size_type beg = s.find_first_not_of(" \t");
2411  if (beg == std::string::npos)
2412  return s;
2413  const std::string::size_type end = s.find_last_not_of(" \t");
2414  if (end == std::string::npos)
2415  return s.substr(beg);
2416  return s.substr(beg, end - beg + 1);
2417 }
2418 
2419 /**
2420  * @brief get parameters from code. For example 'foo(1,2)' => '1','2'
2421  * @param line in: The code
2422  * @param pos in: Position to the '('. out: Position to the ')'
2423  * @param params out: The extracted parameters
2424  * @param numberOfNewlines out: number of newlines in the macro call
2425  * @param endFound out: was the end parentheses found?
2426  */
2427 static void getparams(const std::string &line,
2428  std::string::size_type &pos,
2429  std::vector<std::string> &params,
2430  unsigned int &numberOfNewlines,
2431  bool &endFound)
2432 {
2433  params.clear();
2434  numberOfNewlines = 0;
2435  endFound = false;
2436 
2437  if (line[pos] == ' ')
2438  pos++;
2439 
2440  if (line[pos] != '(')
2441  return;
2442 
2443  // parentheses level
2444  int parlevel = 0;
2445 
2446  // current parameter data
2447  std::string par;
2448 
2449  // scan for parameters..
2450  for (; pos < line.length(); ++pos) {
2451  // increase parentheses level
2452  if (line[pos] == '(') {
2453  ++parlevel;
2454  if (parlevel == 1)
2455  continue;
2456  }
2457 
2458  // decrease parentheses level
2459  else if (line[pos] == ')') {
2460  --parlevel;
2461  if (parlevel <= 0) {
2462  endFound = true;
2463  params.push_back(trim(par));
2464  break;
2465  }
2466  }
2467 
2468  // string
2469  else if (line[pos] == '\"' || line[pos] == '\'') {
2470  const std::string::size_type p = pos;
2471  skipstring(line, pos);
2472  if (pos == line.length())
2473  break;
2474  par += line.substr(p, pos + 1 - p);
2475  continue;
2476  }
2477 
2478  // count newlines. the expanded macro must have the same number of newlines
2479  else if (line[pos] == '\n') {
2480  ++numberOfNewlines;
2481  continue;
2482  }
2483 
2484  // new parameter
2485  if (parlevel == 1 && line[pos] == ',') {
2486  params.push_back(trim(par));
2487  par = "";
2488  }
2489 
2490  // spaces are only added if needed
2491  else if (line[pos] == ' ') {
2492  // Add space only if it is needed
2493  if (par.size() && std::isalnum((unsigned char)par.back())) {
2494  par += ' ';
2495  }
2496  }
2497 
2498  // add character to current parameter
2499  else if (parlevel >= 1 && line[pos] != Preprocessor::macroChar) {
2500  par.append(1, line[pos]);
2501  }
2502  }
2503 }
2504 
2505 /** @brief Class that the preprocessor uses when it expands macros. This class represents a preprocessor macro */
2507 private:
2508  /** tokens of this macro */
2510 
2511  /** macro parameters */
2512  std::vector<std::string> _params;
2513 
2514  /** macro definition in plain text */
2515  const std::string _macro;
2516 
2517  /** does this macro take a variable number of parameters? */
2519 
2520  /** The macro has parentheses but no parameters.. "AAA()" */
2521  bool _nopar;
2522 
2523  /** disabled assignment operator */
2524  void operator=(const PreprocessorMacro &);
2525 
2526  /** @brief expand inner macro */
2527  std::vector<std::string> expandInnerMacros(const std::vector<std::string> &params1,
2528  const std::map<std::string, PreprocessorMacro *> &macros) const {
2529  std::string innerMacroName;
2530 
2531  // Is there an inner macro..
2532  {
2533  const Token *tok = Token::findsimplematch(tokens(), ")");
2534  if (!Token::Match(tok, ") %name% ("))
2535  return params1;
2536  innerMacroName = tok->strAt(1);
2537  tok = tok->tokAt(3);
2538  unsigned int par = 0;
2539  while (Token::Match(tok, "%name% ,|)")) {
2540  tok = tok->tokAt(2);
2541  par++;
2542  }
2543  if (tok || par != params1.size())
2544  return params1;
2545  }
2546 
2547  std::vector<std::string> params2(params1);
2548 
2549  for (std::size_t ipar = 0; ipar < params1.size(); ++ipar) {
2550  const std::string s(innerMacroName + "(");
2551  const std::string param(params1[ipar]);
2552  if (param.compare(0,s.length(),s)==0 && param.back() == ')') {
2553  std::vector<std::string> innerparams;
2554  std::string::size_type pos = s.length() - 1;
2555  unsigned int num = 0;
2556  bool endFound = false;
2557  getparams(param, pos, innerparams, num, endFound);
2558  if (pos == param.length()-1 && num==0 && endFound && innerparams.size() == params1.size()) {
2559  // Is inner macro defined?
2560  std::map<std::string, PreprocessorMacro *>::const_iterator it = macros.find(innerMacroName);
2561  if (it != macros.end()) {
2562  // expand the inner macro
2563  const PreprocessorMacro *innerMacro = it->second;
2564 
2565  std::string innercode;
2566  std::map<std::string,PreprocessorMacro *> innermacros = macros;
2567  innermacros.erase(innerMacroName);
2568  innerMacro->code(innerparams, innermacros, innercode);
2569  params2[ipar] = innercode;
2570  }
2571  }
2572  }
2573  }
2574 
2575  return params2;
2576  }
2577 
2578 public:
2579  /**
2580  * @brief Constructor for PreprocessorMacro. This is the "setter"
2581  * for this class - everything is setup here.
2582  * @param macro The code after define, until end of line,
2583  * e.g. "A(x) foo(x);"
2584  */
2585  PreprocessorMacro(const std::string &macro, const Settings* settings)
2586  : tokenlist(settings), _macro(macro) {
2587 
2588  // Tokenize the macro to make it easier to handle
2589  std::istringstream istr(macro);
2590  tokenlist.createTokens(istr);
2591 
2592  // initialize parameters to default values
2593  _variadic = _nopar = false;
2594 
2595  const std::string::size_type pos = macro.find_first_of(" (");
2596  if (pos != std::string::npos && macro[pos] == '(') {
2597  // Extract macro parameters
2598  if (Token::Match(tokens(), "%name% ( %name%")) {
2599  for (const Token *tok = tokens()->tokAt(2); tok; tok = tok->next()) {
2600  if (tok->str() == ")")
2601  break;
2602  if (Token::simpleMatch(tok, ". . . )")) {
2603  if (tok->previous()->str() == ",")
2604  _params.push_back("__VA_ARGS__");
2605  _variadic = true;
2606  break;
2607  }
2608  if (tok->isName())
2609  _params.push_back(tok->str());
2610  }
2611  }
2612 
2613  else if (Token::Match(tokens(), "%name% ( . . . )"))
2614  _variadic = true;
2615 
2616  else if (Token::Match(tokens(), "%name% ( )"))
2617  _nopar = true;
2618  }
2619  }
2620 
2621  /** return tokens of this macro */
2622  const Token *tokens() const {
2623  return tokenlist.front();
2624  }
2625 
2626  /** read parameters of this macro */
2627  const std::vector<std::string> &params() const {
2628  return _params;
2629  }
2630 
2631  /** check if this is macro has a variable number of parameters */
2632  bool variadic() const {
2633  return _variadic;
2634  }
2635 
2636  /** Check if this macro has parentheses but no parameters */
2637  bool nopar() const {
2638  return _nopar;
2639  }
2640 
2641  /** name of macro */
2642  const std::string &name() const {
2643  return tokens() ? tokens()->str() : emptyString;
2644  }
2645 
2646  /**
2647  * get expanded code for this macro
2648  * @param params2 macro parameters
2649  * @param macros macro definitions (recursion)
2650  * @param macrocode output string
2651  * @return true if the expanding was successful
2652  */
2653  bool code(const std::vector<std::string> &params2, const std::map<std::string, PreprocessorMacro *> &macros, std::string &macrocode) const {
2654  if (_nopar || (_params.empty() && _variadic)) {
2655  macrocode = _macro.substr(1 + _macro.find(')'));
2656  if (macrocode.empty())
2657  return true;
2658 
2659  std::string::size_type pos = 0;
2660  // Remove leading spaces
2661  if ((pos = macrocode.find_first_not_of(" ")) > 0)
2662  macrocode.erase(0, pos);
2663  // Remove ending newline
2664  if ((pos = macrocode.find_first_of("\r\n")) != std::string::npos)
2665  macrocode.erase(pos);
2666 
2667  // Replace "__VA_ARGS__" with parameters
2668  if (!_nopar) {
2669  std::string s;
2670  for (std::size_t i = 0; i < params2.size(); ++i) {
2671  if (i > 0)
2672  s += ",";
2673  s += params2[i];
2674  }
2675 
2676  pos = 0;
2677  while ((pos = macrocode.find("__VA_ARGS__", pos)) != std::string::npos) {
2678  macrocode.erase(pos, 11);
2679  macrocode.insert(pos, s);
2680  pos += s.length();
2681  }
2682  }
2683  }
2684 
2685  else if (_params.empty()) {
2686  std::string::size_type pos = _macro.find_first_of(" \"");
2687  if (pos == std::string::npos)
2688  macrocode = "";
2689  else {
2690  if (_macro[pos] == ' ')
2691  pos++;
2692  macrocode = _macro.substr(pos);
2693  if ((pos = macrocode.find_first_of("\r\n")) != std::string::npos)
2694  macrocode.erase(pos);
2695  }
2696  }
2697 
2698  else {
2699  const std::vector<std::string> givenparams = expandInnerMacros(params2, macros);
2700 
2701  const Token *tok = tokens();
2702  while (tok && tok->str() != ")")
2703  tok = tok->next();
2704  if (tok) {
2705  bool optcomma = false;
2706  while (nullptr != (tok = tok->next())) {
2707  std::string str = tok->str();
2708  if (str[0] == '#' || tok->isName()) {
2709  if (str == "##")
2710  continue;
2711 
2712  const bool stringify(str[0] == '#');
2713  if (stringify) {
2714  str = str.erase(0, 1);
2715  }
2716  for (std::size_t i = 0; i < _params.size(); ++i) {
2717  if (str == _params[i]) {
2718  if (_variadic &&
2719  (i == _params.size() - 1 ||
2720  (givenparams.size() + 2 == _params.size() && i + 1 == _params.size() - 1))) {
2721  str = "";
2722  for (std::size_t j = _params.size() - 1; j < givenparams.size(); ++j) {
2723  if (optcomma || j > _params.size() - 1)
2724  str += ",";
2725  optcomma = false;
2726  str += givenparams[j];
2727  }
2728  } else if (i >= givenparams.size()) {
2729  // Macro had more parameters than caller used.
2730  macrocode = "";
2731  return false;
2732  } else if (stringify) {
2733  const std::string &s(givenparams[i]);
2734  std::ostringstream ostr;
2735  ostr << "\"";
2736  for (std::string::size_type j = 0; j < s.size(); ++j) {
2737  if (s[j] == '\\' || s[j] == '\"')
2738  ostr << '\\';
2739  ostr << s[j];
2740  }
2741  str = ostr.str() + "\"";
2742  } else
2743  str = givenparams[i];
2744 
2745  break;
2746  }
2747  }
2748 
2749  // expand nopar macro
2750  if (tok->strAt(-1) != "##") {
2751  const std::map<std::string, PreprocessorMacro *>::const_iterator it = macros.find(str);
2752  if (it != macros.end() && it->second->_macro.find('(') == std::string::npos) {
2753  str = it->second->_macro;
2754  if (str.find(' ') != std::string::npos)
2755  str.erase(0, str.find(' '));
2756  else
2757  str = "";
2758  }
2759  }
2760  }
2761  if (_variadic && tok->str() == "," && tok->next() && tok->next()->str() == "##") {
2762  optcomma = true;
2763  continue;
2764  }
2765  optcomma = false;
2766  macrocode += str;
2767  if (Token::Match(tok, "%name% %name%") ||
2768  Token::Match(tok, "%name% %num%") ||
2769  Token::Match(tok, "%num% %name%") ||
2770  Token::simpleMatch(tok, "> >"))
2771  macrocode += " ";
2772  }
2773  }
2774  }
2775 
2776  return true;
2777  }
2778 };
2779 
2780 /**
2781  * Get data from a input string. This is an extended version of std::getline.
2782  * The std::getline only get a single line at a time. It can therefore happen that it
2783  * contains a partial statement. This function ensures that the returned data
2784  * doesn't end in the middle of a statement. The "getlines" name indicate that
2785  * this function will return multiple lines if needed.
2786  * @param istr input stream
2787  * @param line output data
2788  * @return success
2789  */
2790 static bool getlines(std::istream &istr, std::string &line)
2791 {
2792  if (!istr.good())
2793  return false;
2794  line = "";
2795  int parlevel = 0;
2796  bool directive = false;
2797  for (char ch = (char)istr.get(); istr.good(); ch = (char)istr.get()) {
2798  if (ch == '\'' || ch == '\"') {
2799  line += ch;
2800  char c = 0;
2801  while (istr.good() && c != ch) {
2802  if (c == '\\') {
2803  c = (char)istr.get();
2804  if (!istr.good())
2805  return true;
2806  line += c;
2807  }
2808 
2809  c = (char)istr.get();
2810  if (!istr.good())
2811  return true;
2812  if (c == '\n' && directive)
2813  return true;
2814  line += c;
2815  }
2816  continue;
2817  }
2818  if (ch == '(')
2819  ++parlevel;
2820  else if (ch == ')')
2821  --parlevel;
2822  else if (ch == '\n') {
2823  if (directive)
2824  return true;
2825 
2826  if (istr.peek() == '#') {
2827  line += ch;
2828  return true;
2829  }
2830  } else if (!directive && parlevel <= 0 && ch == ';') {
2831  line += ";";
2832  return true;
2833  }
2834 
2835  if (ch == '#' && line.empty())
2836  directive = true;
2837  line += ch;
2838  }
2839  return true;
2840 }
2841 
2842 bool Preprocessor::validateCfg(const std::string &code, const std::string &cfg)
2843 {
2844  const bool printInformation = _settings.isEnabled("information");
2845 
2846  // fill up "macros" with empty configuration macros
2847  std::set<std::string> macros;
2848  for (std::string::size_type pos = 0; pos < cfg.size();) {
2849  const std::string::size_type pos2 = cfg.find_first_of(";=", pos);
2850  if (pos2 == std::string::npos) {
2851  macros.insert(cfg.substr(pos));
2852  break;
2853  }
2854  if (cfg[pos2] == ';')
2855  macros.insert(cfg.substr(pos, pos2-pos));
2856  pos = cfg.find(';', pos2);
2857  if (pos != std::string::npos)
2858  ++pos;
2859  }
2860 
2861  // check if any empty macros are used in code
2862  for (std::set<std::string>::const_iterator it = macros.begin(); it != macros.end(); ++it) {
2863  const std::string &macro = *it;
2864  std::string::size_type pos = 0;
2865  while ((pos = code.find_first_of(std::string("#\"'")+macro[0], pos)) != std::string::npos) {
2866  const std::string::size_type pos1 = pos;
2867  const std::string::size_type pos2 = pos + macro.size();
2868  pos++;
2869 
2870  // skip string..
2871  if (code[pos1] == '\"' || code[pos1] == '\'') {
2872  while (pos < code.size() && code[pos] != code[pos1]) {
2873  if (code[pos] == '\\')
2874  ++pos;
2875  ++pos;
2876  }
2877  ++pos;
2878  }
2879 
2880  // skip preprocessor statement..
2881  else if (code[pos1] == '#') {
2882  if (pos1 == 0 || code[pos1-1] == '\n')
2883  pos = code.find('\n', pos);
2884  }
2885 
2886  // is macro used in code?
2887  else if (code.compare(pos1,macro.size(),macro) == 0) {
2888  if (pos1 > 0 && (std::isalnum((unsigned char)code[pos1-1U]) || code[pos1-1U] == '_'))
2889  continue;
2890  if (pos2 < code.size() && (std::isalnum((unsigned char)code[pos2]) || code[pos2] == '_'))
2891  continue;
2892  // macro is used in code, return false
2893  if (printInformation)
2894  validateCfgError(cfg, macro);
2895  return false;
2896  }
2897  }
2898  }
2899 
2900  return true;
2901 }
2902 
2903 void Preprocessor::validateCfgError(const std::string &cfg, const std::string &macro)
2904 {
2905  const std::string id = "ConfigurationNotChecked";
2906  std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
2908  locationList.push_back(loc);
2909  ErrorLogger::ErrorMessage errmsg(locationList, Severity::information, "Skipping configuration '" + cfg + "' since the value of '" + macro + "' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly.", id, false);
2910  _errorLogger->reportInfo(errmsg);
2911 }
2912 
2913 std::string Preprocessor::expandMacros(const std::string &code, std::string filename, const std::string &cfg, ErrorLogger *errorLogger)
2914 {
2915  // Search for macros and expand them..
2916  // --------------------------------------------
2917 
2918  // Available macros (key=macroname, value=macro).
2919  std::map<std::string, PreprocessorMacro *> macros;
2920  const Settings settings;
2921 
2922  {
2923  // fill up "macros" with user defined macros
2924  const std::map<std::string,std::string> cfgmap(getcfgmap(cfg,nullptr,""));
2925  std::map<std::string, std::string>::const_iterator it;
2926  for (it = cfgmap.begin(); it != cfgmap.end(); ++it) {
2927  std::string s = it->first;
2928  if (!it->second.empty())
2929  s += " " + it->second;
2930  PreprocessorMacro *macro = new PreprocessorMacro(s, &settings);
2931  macros[it->first] = macro;
2932  }
2933  }
2934 
2935  // Current line number
2936  unsigned int linenr = 1;
2937 
2938  // linenr, filename
2939  std::stack< std::pair<unsigned int, std::string> > fileinfo;
2940 
2941  // output stream
2942  std::ostringstream ostr;
2943 
2944  // read code..
2945  std::istringstream istr(code);
2946  std::string line;
2947  while (getlines(istr, line)) {
2948  if (line.empty())
2949  continue;
2950 
2951  // Preprocessor directive
2952  if (line[0] == '#') {
2953  // defining a macro..
2954  if (line.compare(0, 8, "#define ") == 0) {
2955  PreprocessorMacro *macro = new PreprocessorMacro(line.substr(8), &settings);
2956  if (macro->name().empty() || macro->name() == "NULL") {
2957  delete macro;
2958  } else if (macro->name() == "BOOST_FOREACH") {
2959  // BOOST_FOREACH is currently too complex to parse, so skip it.
2960  delete macro;
2961  } else {
2962  std::map<std::string, PreprocessorMacro *>::iterator it;
2963  it = macros.find(macro->name());
2964  if (it != macros.end())
2965  delete it->second;
2966  macros[macro->name()] = macro;
2967  }
2968  line = "\n";
2969  }
2970 
2971  // undefining a macro..
2972  else if (line.compare(0, 7, "#undef ") == 0) {
2973  std::map<std::string, PreprocessorMacro *>::iterator it;
2974  it = macros.find(line.substr(7));
2975  if (it != macros.end()) {
2976  delete it->second;
2977  macros.erase(it);
2978  }
2979  line = "\n";
2980  }
2981 
2982  // entering a file, update position..
2983  else if (line.compare(0, 7, "#file \"") == 0) {
2984  fileinfo.push(std::pair<unsigned int, std::string>(linenr, filename));
2985  filename = line.substr(7, line.length() - 8);
2986  linenr = 0;
2987  line += "\n";
2988  }
2989 
2990  // leaving a file, update position..
2991  else if (line == "#endfile") {
2992  if (!fileinfo.empty()) {
2993  linenr = fileinfo.top().first;
2994  filename = fileinfo.top().second;
2995  fileinfo.pop();
2996  }
2997  line += "\n";
2998  }
2999 
3000  // all other preprocessor directives are just replaced with a newline
3001  else
3002  line += "\n";
3003  }
3004 
3005  // expand macros..
3006  else {
3007  // Limit for each macro.
3008  // The limit specify a position in the "line" variable.
3009  // For a "recursive macro" where the expanded text contains
3010  // the macro again, the macro should not be expanded again.
3011  // The limits are used to prevent recursive expanding.
3012  // * When a macro is expanded its limit position is set to
3013  // the last expanded character.
3014  // * macros are only allowed to be expanded when the
3015  // the position is beyond the limit.
3016  // * The limit is relative to the end of the "line"
3017  // variable. Inserting and deleting text before the limit
3018  // without updating the limit is safe.
3019  // * when pos goes beyond a limit the limit needs to be
3020  // deleted because it is unsafe to insert/delete text
3021  // after the limit otherwise
3022  std::map<const PreprocessorMacro *, std::size_t> limits;
3023 
3024  // pos is the current position in line
3025  std::string::size_type pos = 0;
3026 
3027  // scan line to see if there are any macros to expand..
3028  unsigned int tmpLinenr = 0;
3029  while (pos < line.size()) {
3030  if (line[pos] == '\n')
3031  ++tmpLinenr;
3032 
3033  // skip strings..
3034  if (line[pos] == '\"' || line[pos] == '\'') {
3035  const char ch = line[pos];
3036 
3037  skipstring(line, pos);
3038  ++pos;
3039 
3040  if (pos >= line.size()) {
3041  writeError(filename,
3042  linenr + tmpLinenr,
3043  errorLogger,
3044  "noQuoteCharPair",
3045  std::string("No pair for character (") + ch + "). Can't process file. File is either invalid or unicode, which is currently not supported.");
3046 
3047  std::map<std::string, PreprocessorMacro *>::iterator it;
3048  for (it = macros.begin(); it != macros.end(); ++it)
3049  delete it->second;
3050  macros.clear();
3051  return "";
3052  }
3053 
3054  continue;
3055  }
3056 
3057  if (!std::isalpha((unsigned char)line[pos]) && line[pos] != '_')
3058  ++pos;
3059 
3060  // found an identifier..
3061  // the "while" is used in case the expanded macro will immediately call another macro
3062  while (pos < line.length() && (std::isalpha((unsigned char)line[pos]) || line[pos] == '_')) {
3063  // pos1 = start position of macro
3064  const std::string::size_type pos1 = pos++;
3065 
3066  // find the end of the identifier
3067  while (pos < line.size() && (std::isalnum((unsigned char)line[pos]) || line[pos] == '_'))
3068  ++pos;
3069 
3070  // get identifier
3071  const std::string id = line.substr(pos1, pos - pos1);
3072 
3073  // is there a macro with this name?
3074  std::map<std::string, PreprocessorMacro *>::const_iterator it;
3075  it = macros.find(id);
3076  if (it == macros.end())
3077  break; // no macro with this name exist
3078 
3079  const PreprocessorMacro * const macro = it->second;
3080 
3081  // check that pos is within allowed limits for this
3082  // macro
3083  {
3084  const std::map<const PreprocessorMacro *, std::size_t>::const_iterator it2 = limits.find(macro);
3085  if (it2 != limits.end() && pos <= line.length() - it2->second)
3086  break;
3087  }
3088 
3089  // get parameters from line..
3090  if (macro->params().size() && pos >= line.length())
3091  break;
3092  std::vector<std::string> params;
3093  std::string::size_type pos2 = pos;
3094 
3095  // number of newlines within macro use
3096  unsigned int numberOfNewlines = 0;
3097 
3098  // if the macro has parentheses, get parameters
3099  if (macro->variadic() || macro->nopar() || macro->params().size()) {
3100  // is the end parentheses found?
3101  bool endFound = false;
3102 
3103  getparams(line,pos2,params,numberOfNewlines,endFound);
3104 
3105  // something went wrong so bail out
3106  if (!endFound)
3107  break;
3108  }
3109 
3110  // Just an empty parameter => clear
3111  if (params.size() == 1 && params[0] == "")
3112  params.clear();
3113 
3114  // Check that it's the same number of parameters..
3115  if (!macro->variadic() && params.size() != macro->params().size())
3116  break;
3117 
3118  // Create macro code..
3119  std::string tempMacro;
3120  if (!macro->code(params, macros, tempMacro)) {
3121  // Syntax error in code
3122  writeError(filename,
3123  linenr + tmpLinenr,
3124  errorLogger,
3125  "syntaxError",
3126  std::string("Syntax error. Not enough parameters for macro '") + macro->name() + "'.");
3127 
3128  std::map<std::string, PreprocessorMacro *>::iterator iter;
3129  for (iter = macros.begin(); iter != macros.end(); ++iter)
3130  delete iter->second;
3131  macros.clear();
3132  return "";
3133  }
3134 
3135  // make sure number of newlines remain the same..
3136  std::string macrocode(std::string(numberOfNewlines, '\n') + tempMacro);
3137 
3138  // Insert macro code..
3139  if (macro->variadic() || macro->nopar() || !macro->params().empty())
3140  ++pos2;
3141 
3142  // Remove old limits
3143  for (std::map<const PreprocessorMacro *, std::size_t>::iterator iter = limits.begin();
3144  iter != limits.end();) {
3145  if ((line.length() - pos1) < iter->second) {
3146  // We have gone past this limit, so just delete it
3147  limits.erase(iter++);
3148  } else {
3149  ++iter;
3150  }
3151  }
3152 
3153  // don't allow this macro to be expanded again before pos2
3154  limits[macro] = line.length() - pos2;
3155 
3156  // erase macro
3157  line.erase(pos1, pos2 - pos1);
3158 
3159  // Don't glue this macro into variable or number after it
3160  if (!line.empty() && (std::isalnum((unsigned char)line[pos1]) || line[pos1] == '_'))
3161  macrocode.append(1,' ');
3162 
3163  // insert macrochar before each symbol/nr/operator
3164  bool str = false;
3165  bool chr = false;
3166  for (std::size_t i = 0U; i < macrocode.size(); ++i) {
3167  if (macrocode[i] == '\\') {
3168  i++;
3169  continue;
3170  } else if (macrocode[i] == '\"')
3171  str = !str;
3172  else if (macrocode[i] == '\'')
3173  chr = !chr;
3174  else if (str || chr)
3175  continue;
3176  else if (macrocode[i] == '.') { // 5. / .5
3177  if ((i > 0U && std::isdigit((unsigned char)macrocode[i-1])) ||
3178  (i+1 < macrocode.size() && std::isdigit((unsigned char)macrocode[i+1]))) {
3179  if (i > 0U && !std::isdigit((unsigned char)macrocode[i-1])) {
3180  macrocode.insert(i, 1U, macroChar);
3181  i++;
3182  }
3183  i++;
3184  if (i<macrocode.size() && std::isdigit((unsigned char)macrocode[i]))
3185  i++;
3186  if (i+1U < macrocode.size() &&
3187  (macrocode[i] == 'e' || macrocode[i] == 'E') &&
3188  (macrocode[i+1] == '+' || macrocode[i+1] == '-')) {
3189  i+=2;
3190  }
3191  }
3192  } else if (std::isalnum((unsigned char)macrocode[i]) || macrocode[i] == '_') {
3193  if ((i > 0U) &&
3194  (!std::isalnum((unsigned char)macrocode[i-1])) &&
3195  (macrocode[i-1] != '_') &&
3196  (macrocode[i-1] != macroChar)) {
3197  macrocode.insert(i, 1U, macroChar);
3198  }
3199 
3200  // 1e-7 / 1e+7
3201  if (i+3U < macrocode.size() &&
3202  (std::isdigit((unsigned char)macrocode[i]) || macrocode[i]=='.') &&
3203  (macrocode[i+1] == 'e' || macrocode[i+1] == 'E') &&
3204  (macrocode[i+2] == '-' || macrocode[i+2] == '+') &&
3205  std::isdigit((unsigned char)macrocode[i+3])) {
3206  i += 3U;
3207  }
3208 
3209  // 1.f / 1.e7
3210  if (i+2U < macrocode.size() &&
3211  std::isdigit((unsigned char)macrocode[i]) &&
3212  macrocode[i+1] == '.' &&
3213  std::isalpha((unsigned char)macrocode[i+2])) {
3214  i += 2U;
3215  if (i+2U < macrocode.size() &&
3216  (macrocode[i+0] == 'e' || macrocode[i+0] == 'E') &&
3217  (macrocode[i+1] == '-' || macrocode[i+1] == '+') &&
3218  std::isdigit((unsigned char)macrocode[i+2])) {
3219  i += 2U;
3220  }
3221  }
3222  }
3223  }
3224  line.insert(pos1, macroChar + macrocode);
3225 
3226  // position = start position.
3227  pos = pos1;
3228  }
3229  }
3230  }
3231 
3232  // the line has been processed in various ways. Now add it to the output stream
3233  ostr << line;
3234 
3235  // update linenr
3236  for (std::string::size_type p = 0; p < line.length(); ++p) {
3237  if (line[p] == '\n')
3238  ++linenr;
3239  }
3240  }
3241 
3242  for (std::map<std::string, PreprocessorMacro *>::iterator it = macros.begin(); it != macros.end(); ++it)
3243  delete it->second;
3244  macros.clear();
3245 
3246  return ostr.str();
3247 }
3248 
3249 
3250 void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
3251 {
3252  Settings settings2(*settings);
3253  Preprocessor preprocessor(settings2, errorLogger);
3254  settings2.checkConfiguration=true;
3255  preprocessor.missingInclude("", 1, "", UserHeader);
3256  preprocessor.missingInclude("", 1, "", SystemHeader);
3257  preprocessor.validateCfgError("X", "X");
3258  preprocessor.error("", 1, "#error message"); // #error ..
3259 }