2009-01-24 20:28:30 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2014-02-15 07:45:39 +01:00
* Copyright ( C ) 2007 - 2014 Daniel Marjamäki and Cppcheck team .
2009-01-24 20:28:30 +01:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2009-09-27 17:08:31 +02:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2009-01-24 20:28:30 +01:00
*/
# include "preprocessor.h"
# include "tokenize.h"
# include "token.h"
2010-07-21 16:00:12 +02:00
# include "path.h"
2010-07-24 22:12:56 +02:00
# include "errorlogger.h"
2010-07-26 22:05:17 +02:00
# include "settings.h"
2009-01-24 20:28:30 +01:00
# include <algorithm>
# include <sstream>
# include <fstream>
# include <cstdlib>
# include <cctype>
2009-04-05 20:14:02 +02:00
# include <vector>
2009-07-22 18:47:50 +02:00
# include <set>
2009-12-09 17:13:48 +01:00
# include <stack>
2009-01-24 20:28:30 +01:00
2011-05-02 14:58:16 +02:00
bool Preprocessor : : missingIncludeFlag ;
2014-05-03 19:31:15 +02:00
bool Preprocessor : : missingSystemIncludeFlag ;
2011-05-02 14:58:16 +02:00
2012-03-27 19:35:41 +02:00
char Preprocessor : : macroChar = char ( 1 ) ;
2014-06-01 11:24:10 +02:00
Preprocessor : : Preprocessor ( Settings * settings , ErrorLogger * errorLogger ) : _settings ( settings ) , _errorLogger ( errorLogger ) , _foundUnhandledChars ( false )
2009-06-26 13:19:55 +02:00
{
}
2010-08-06 19:38:21 +02:00
void Preprocessor : : writeError ( const std : : string & fileName , const unsigned int linenr , ErrorLogger * errorLogger , const std : : string & errorType , const std : : string & errorText )
2009-05-19 21:19:15 +02:00
{
2010-04-02 07:30:58 +02:00
if ( ! errorLogger )
2009-05-19 21:19:15 +02:00
return ;
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
ErrorLogger : : ErrorMessage : : FileLocation loc ;
2009-12-09 17:13:48 +01:00
loc . line = linenr ;
2010-07-17 00:27:40 +02:00
loc . setfile ( fileName ) ;
2009-05-19 21:19:15 +02:00
locationList . push_back ( loc ) ;
2009-12-09 17:13:48 +01:00
errorLogger - > reportErr ( ErrorLogger : : ErrorMessage ( locationList ,
2010-07-14 22:11:32 +02:00
Severity : : error ,
2009-12-09 17:13:48 +01:00
errorText ,
2011-04-14 18:02:01 +02:00
errorType ,
false ) ) ;
2009-05-19 21:19:15 +02:00
}
2012-06-10 11:00:27 +02:00
static unsigned char readChar ( std : : istream & istr , unsigned int bom )
2009-01-24 20:28:30 +01:00
{
2009-12-22 22:44:21 +01:00
unsigned char ch = ( unsigned char ) istr . get ( ) ;
2009-01-24 20:28:30 +01:00
2012-06-10 11:00:27 +02:00
// For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the
// character is non-ASCII character then replace it with 0xff
if ( bom = = 0xfeff | | bom = = 0xfffe ) {
unsigned char ch2 = ( unsigned char ) istr . get ( ) ;
int ch16 = ( bom = = 0xfeff ) ? ( ch < < 8 | ch2 ) : ( ch2 < < 8 | ch ) ;
ch = ( unsigned char ) ( ( ch16 > = 0x80 ) ? 0xff : ch16 ) ;
}
2009-01-24 20:28:30 +01:00
// Handling of newlines..
2011-10-13 20:53:06 +02:00
if ( ch = = ' \r ' ) {
2009-01-24 20:28:30 +01:00
ch = ' \n ' ;
2012-06-10 11:00:27 +02:00
if ( bom = = 0 & & ( char ) istr . peek ( ) = = ' \n ' )
2009-01-24 20:28:30 +01:00
( void ) istr . get ( ) ;
2012-06-10 11:00:27 +02:00
else if ( bom = = 0xfeff | | bom = = 0xfffe ) {
int c1 = istr . get ( ) ;
int c2 = istr . get ( ) ;
int ch16 = ( bom = = 0xfeff ) ? ( c1 < < 8 | c2 ) : ( c2 < < 8 | c1 ) ;
if ( ch16 ! = ' \n ' ) {
istr . unget ( ) ;
istr . unget ( ) ;
}
}
2009-01-24 20:28:30 +01:00
}
return ch ;
}
2012-01-02 15:52:19 +01:00
// Concatenates a list of strings, inserting a separator between parts
static std : : string join ( const std : : set < std : : string > & list , char separator )
{
std : : string s ;
for ( std : : set < std : : string > : : const_iterator it = list . begin ( ) ; it ! = list . end ( ) ; + + it ) {
if ( ! s . empty ( ) )
s + = separator ;
s + = * it ;
}
return s ;
}
// Removes duplicate string portions separated by the specified separator
static std : : string unify ( const std : : string & s , char separator )
2010-04-15 18:37:51 +02:00
{
2012-01-02 15:52:19 +01:00
std : : set < std : : string > parts ;
2010-04-15 18:37:51 +02:00
std : : string : : size_type prevPos = 0 ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type pos = 0 ; pos < s . length ( ) ; + + pos ) {
if ( s [ pos ] = = separator ) {
2010-04-15 18:37:51 +02:00
if ( pos > prevPos )
2012-01-02 15:52:19 +01:00
parts . insert ( s . substr ( prevPos , pos - prevPos ) ) ;
2010-04-15 18:37:51 +02:00
prevPos = pos + 1 ;
}
}
if ( prevPos < s . length ( ) )
2012-01-02 15:52:19 +01:00
parts . insert ( s . substr ( prevPos ) ) ;
2010-04-15 18:37:51 +02:00
2012-01-02 15:52:19 +01:00
return join ( parts , separator ) ;
2010-04-15 18:37:51 +02:00
}
2013-06-19 17:42:55 +02:00
2013-11-15 19:21:21 +01:00
bool Preprocessor : : cplusplus ( const Settings * settings , const std : : string & filename )
{
const bool undef = settings & & settings - > userUndefs . find ( " __cplusplus " ) ! = settings - > userUndefs . end ( ) ;
const bool cpplang = settings & & settings - > enforcedLang = = Settings : : CPP ;
const bool cppfile = ( ! settings | | settings - > enforcedLang = = Settings : : None ) & & Path : : isCPP ( filename ) ;
return ( ! undef & & ( cpplang | | cppfile ) ) ;
}
2013-06-19 17:42:55 +02:00
/**
* Get cfgmap - a map of macro names and values
*/
2013-11-15 19:21:21 +01:00
static std : : map < std : : string , std : : string > getcfgmap ( const std : : string & cfg , const Settings * settings , const std : : string & filename )
2013-06-19 17:42:55 +02:00
{
std : : map < std : : string , std : : string > cfgmap ;
if ( ! cfg . empty ( ) ) {
std : : string : : size_type pos = 0 ;
for ( ; ; ) {
std : : string : : size_type pos2 = cfg . find_first_of ( " ;= " , pos ) ;
if ( pos2 = = std : : string : : npos ) {
cfgmap [ cfg . substr ( pos ) ] = " " ;
break ;
}
if ( cfg [ pos2 ] = = ' ; ' ) {
cfgmap [ cfg . substr ( pos , pos2 - pos ) ] = " " ;
} else {
std : : string : : size_type pos3 = pos2 ;
pos2 = cfg . find ( " ; " , pos2 ) ;
if ( pos2 = = std : : string : : npos ) {
cfgmap [ cfg . substr ( pos , pos3 - pos ) ] = cfg . substr ( pos3 + 1 ) ;
break ;
} else {
cfgmap [ cfg . substr ( pos , pos3 - pos ) ] = cfg . substr ( pos3 + 1 , pos2 - pos3 - 1 ) ;
}
}
pos = pos2 + 1 ;
}
}
2013-11-15 19:21:21 +01:00
if ( cfgmap . find ( " __cplusplus " ) = = cfgmap . end ( ) & & Preprocessor : : cplusplus ( settings , filename ) )
cfgmap [ " __cplusplus " ] = " 1 " ;
2013-06-19 17:42:55 +02:00
return cfgmap ;
}
2009-01-24 20:28:30 +01:00
/** Just read the code into a string. Perform simple cleanup of the code */
2012-01-05 18:37:15 +01:00
std : : string Preprocessor : : read ( std : : istream & istr , const std : : string & filename )
2009-01-24 20:28:30 +01:00
{
2012-06-10 11:00:27 +02:00
// The UTF-16 BOM is 0xfffe or 0xfeff.
unsigned int bom = 0 ;
if ( istr . peek ( ) > = 0xfe ) {
2012-09-16 16:41:15 +02:00
bom = ( ( unsigned int ) istr . get ( ) < < 8 ) ;
2012-06-10 11:00:27 +02:00
if ( istr . peek ( ) > = 0xfe )
2012-09-16 16:41:15 +02:00
bom | = ( unsigned int ) istr . get ( ) ;
2013-08-31 13:17:57 +02:00
else
bom = 0 ; // allowed boms are 0/0xfffe/0xfeff
2012-06-10 11:00:27 +02:00
}
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2013-09-01 07:13:48 +02:00
if ( _settings & & _settings - > checkConfiguration )
return readpreprocessor ( istr , bom ) ;
2011-02-11 06:30:42 +01:00
// ------------------------------------------------------------------------------------------
//
2011-11-13 20:27:56 +01:00
// handling <backslash><newline>
// when this is encountered the <backslash><newline> will be "skipped".
2009-01-24 20:28:30 +01:00
// on the next <newline>, extra newlines will be added
std : : ostringstream code ;
2011-02-11 06:30:42 +01:00
unsigned int newlines = 0 ;
2012-06-10 11:00:27 +02:00
for ( unsigned char ch = readChar ( istr , bom ) ; istr . good ( ) ; ch = readChar ( istr , bom ) ) {
2009-01-24 20:28:30 +01:00
// Replace assorted special chars with spaces..
2010-04-02 07:30:58 +02:00
if ( ( ( ch & 0x80 ) = = 0 ) & & ( ch ! = ' \n ' ) & & ( std : : isspace ( ch ) | | std : : iscntrl ( ch ) ) )
2009-01-24 20:28:30 +01:00
ch = ' ' ;
2011-11-13 20:27:56 +01:00
// <backslash><newline>..
2010-08-06 08:50:31 +02:00
// for gcc-compatibility the trailing spaces should be ignored
// for vs-compatibility the trailing spaces should be kept
// See tickets #640 and #1869
// The solution for now is to have a compiler-dependent behaviour.
2011-10-13 20:53:06 +02:00
if ( ch = = ' \\ ' ) {
2010-08-06 08:50:31 +02:00
unsigned char chNext ;
2012-12-02 20:17:25 +01:00
std : : string spaces ;
2012-12-02 18:04:19 +01:00
2010-08-06 08:50:31 +02:00
# ifdef __GNUC__
// gcc-compatibility: ignore spaces
2012-12-02 20:17:25 +01:00
for ( ; ; spaces + = ' ' ) {
2009-12-22 22:44:21 +01:00
chNext = ( unsigned char ) istr . peek ( ) ;
2010-04-02 07:30:58 +02:00
if ( chNext ! = ' \n ' & & chNext ! = ' \r ' & &
2011-10-13 20:53:06 +02:00
( std : : isspace ( chNext ) | | std : : iscntrl ( chNext ) ) ) {
2011-11-13 20:27:56 +01:00
// Skip whitespace between <backslash> and <newline>
2012-06-10 11:00:27 +02:00
( void ) readChar ( istr , bom ) ;
2009-05-13 21:18:02 +02:00
continue ;
}
break ;
}
2010-08-06 08:50:31 +02:00
# else
// keep spaces
chNext = ( unsigned char ) istr . peek ( ) ;
# endif
2011-10-13 20:53:06 +02:00
if ( chNext = = ' \n ' | | chNext = = ' \r ' ) {
2009-05-13 21:18:02 +02:00
+ + newlines ;
2012-06-10 11:00:27 +02:00
( void ) readChar ( istr , bom ) ; // Skip the "<backslash><newline>"
2012-12-02 20:17:25 +01:00
} else {
code < < " \\ " < < spaces ;
}
2011-10-13 20:53:06 +02:00
} else {
2010-08-06 19:38:21 +02:00
code < < char ( ch ) ;
2009-05-13 21:18:02 +02:00
2011-11-13 20:27:56 +01:00
// if there has been <backslash><newline> sequences, add extra newlines..
2011-10-13 20:53:06 +02:00
if ( ch = = ' \n ' & & newlines > 0 ) {
2009-05-13 21:18:02 +02:00
code < < std : : string ( newlines , ' \n ' ) ;
newlines = 0 ;
}
}
}
2011-02-11 06:30:42 +01:00
std : : string result = code . str ( ) ;
code . str ( " " ) ;
// ------------------------------------------------------------------------------------------
//
// Remove all comments..
2012-01-06 08:09:53 +01:00
result = removeComments ( result , filename ) ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2009-05-13 21:18:02 +02:00
2011-02-11 06:30:42 +01:00
// ------------------------------------------------------------------------------------------
//
// Clean up all preprocessor statements
result = preprocessCleanupDirectives ( result ) ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2011-02-11 06:30:42 +01:00
// ------------------------------------------------------------------------------------------
//
2011-03-30 16:45:31 +02:00
// Clean up preprocessor #if statements with Parentheses
result = removeParentheses ( result ) ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2011-02-11 06:30:42 +01:00
2011-03-01 07:50:17 +01:00
// Remove '#if 0' blocks
if ( result . find ( " #if 0 \n " ) ! = std : : string : : npos )
result = removeIf0 ( result ) ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2011-03-01 07:50:17 +01:00
2011-02-11 06:30:42 +01:00
return result ;
}
2013-09-01 07:13:48 +02:00
/** read preprocessor statements */
std : : string Preprocessor : : readpreprocessor ( std : : istream & istr , const unsigned int bom ) const
{
enum { NEWLINE , SPACE , PREPROCESSOR , BACKSLASH , OTHER } state = NEWLINE ;
std : : ostringstream code ;
unsigned int newlines = 1 ;
2013-09-21 18:48:48 +02:00
unsigned char chPrev = ' ' ;
2013-09-01 07:13:48 +02:00
for ( unsigned char ch = readChar ( istr , bom ) ; istr . good ( ) ; ch = readChar ( istr , bom ) ) {
// Replace assorted special chars with spaces..
if ( ( ( ch & 0x80 ) = = 0 ) & & ( ch ! = ' \n ' ) & & ( std : : isspace ( ch ) | | std : : iscntrl ( ch ) ) )
ch = ' ' ;
if ( ch = = ' ' & & chPrev = = ' ' )
continue ;
if ( state = = PREPROCESSOR & & chPrev = = ' / ' & & ( ch = = ' / ' | | ch = = ' * ' ) )
state = OTHER ;
chPrev = ch ;
if ( ch = = ' \n ' ) {
if ( state ! = BACKSLASH ) {
state = NEWLINE ;
code < < std : : string ( newlines , ' \n ' ) ;
newlines = 1 ;
} else {
+ + newlines ;
state = PREPROCESSOR ;
}
continue ;
}
switch ( state ) {
case NEWLINE :
if ( ch = = ' ' )
state = SPACE ;
else if ( ch = = ' # ' ) {
state = PREPROCESSOR ;
code < < ch ;
} else
state = OTHER ;
break ;
case SPACE :
if ( ch = = ' # ' ) {
state = PREPROCESSOR ;
code < < ch ;
} else if ( ch ! = ' ' )
state = OTHER ;
break ;
case PREPROCESSOR :
code < < ch ;
if ( ch = = ' \\ ' )
state = BACKSLASH ;
break ;
case BACKSLASH :
code < < ch ;
if ( ch ! = ' ' )
state = PREPROCESSOR ;
break ;
case OTHER :
break ;
} ;
}
std : : string result = preprocessCleanupDirectives ( code . str ( ) ) ;
result = removeParentheses ( result ) ;
return removeIf0 ( result ) ;
}
2012-09-01 10:32:27 +02:00
std : : string Preprocessor : : preprocessCleanupDirectives ( const std : : string & processedFile )
2011-02-11 06:30:42 +01:00
{
std : : ostringstream code ;
std : : istringstream sstr ( processedFile ) ;
std : : string line ;
2011-10-13 20:53:06 +02:00
while ( std : : getline ( sstr , line ) ) {
2011-02-11 06:30:42 +01:00
// Trim lines..
if ( ! line . empty ( ) & & line [ 0 ] = = ' ' )
line . erase ( 0 , line . find_first_not_of ( " " ) ) ;
if ( ! line . empty ( ) & & line [ line . size ( ) - 1 ] = = ' ' )
line . erase ( line . find_last_not_of ( " " ) + 1 ) ;
// Preprocessor
2011-10-13 20:53:06 +02:00
if ( ! line . empty ( ) & & line [ 0 ] = = ' # ' ) {
enum {
2011-02-11 06:30:42 +01:00
ESC_NONE ,
ESC_SINGLE ,
ESC_DOUBLE
} escapeStatus = ESC_NONE ;
char prev = ' ' ; // hack to make it skip spaces between # and the directive
code < < " # " ;
std : : string : : const_iterator i = line . begin ( ) ;
2011-02-13 23:57:07 +01:00
+ + i ;
2011-02-11 06:30:42 +01:00
// need space.. #if( => #if (
bool needSpace = true ;
2011-10-13 20:53:06 +02:00
while ( i ! = line . end ( ) ) {
2011-02-11 06:30:42 +01:00
// disable esc-mode
2011-10-13 20:53:06 +02:00
if ( escapeStatus ! = ESC_NONE ) {
if ( prev ! = ' \\ ' & & escapeStatus = = ESC_SINGLE & & * i = = ' \' ' ) {
2011-02-11 06:30:42 +01:00
escapeStatus = ESC_NONE ;
}
2011-10-13 20:53:06 +02:00
if ( prev ! = ' \\ ' & & escapeStatus = = ESC_DOUBLE & & * i = = ' " ' ) {
2011-02-11 06:30:42 +01:00
escapeStatus = ESC_NONE ;
}
2011-10-13 20:53:06 +02:00
} else {
2011-02-11 06:30:42 +01:00
// enable esc-mode
if ( escapeStatus = = ESC_NONE & & * i = = ' " ' )
escapeStatus = ESC_DOUBLE ;
if ( escapeStatus = = ESC_NONE & & * i = = ' \' ' )
escapeStatus = ESC_SINGLE ;
}
// skip double whitespace between arguments
2011-10-13 20:53:06 +02:00
if ( escapeStatus = = ESC_NONE & & prev = = ' ' & & * i = = ' ' ) {
2011-02-13 23:57:07 +01:00
+ + i ;
2011-02-11 06:30:42 +01:00
continue ;
}
// Convert #if( to "#if ("
2011-10-13 20:53:06 +02:00
if ( escapeStatus = = ESC_NONE ) {
if ( needSpace ) {
2011-02-11 06:30:42 +01:00
if ( * i = = ' ( ' | | * i = = ' ! ' )
code < < " " ;
2014-03-18 21:41:47 +01:00
else if ( ! std : : isalpha ( ( unsigned char ) * i ) )
2011-02-11 06:30:42 +01:00
needSpace = false ;
}
if ( * i = = ' # ' )
needSpace = true ;
}
code < < * i ;
2011-10-13 20:53:06 +02:00
if ( escapeStatus ! = ESC_NONE & & prev = = ' \\ ' & & * i = = ' \\ ' ) {
2011-02-11 06:30:42 +01:00
prev = ' ' ;
2011-10-13 20:53:06 +02:00
} else {
2011-02-11 06:30:42 +01:00
prev = * i ;
}
2011-02-13 23:57:07 +01:00
+ + i ;
2011-02-11 06:30:42 +01:00
}
2011-10-13 20:53:06 +02:00
if ( escapeStatus ! = ESC_NONE ) {
2011-02-11 06:30:42 +01:00
// unmatched quotes.. compiler should probably complain about this..
}
2011-10-13 20:53:06 +02:00
} else {
2011-02-11 06:30:42 +01:00
// Do not mess with regular code..
code < < line ;
}
code < < ( sstr . eof ( ) ? " " : " \n " ) ;
}
return code . str ( ) ;
2009-05-13 21:18:02 +02:00
}
2009-10-10 09:29:06 +02:00
static bool hasbom ( const std : : string & str )
{
2012-01-02 12:58:34 +01:00
return bool ( str . size ( ) > = 3 & &
2009-10-11 16:38:55 +02:00
static_cast < unsigned char > ( str [ 0 ] ) = = 0xef & &
static_cast < unsigned char > ( str [ 1 ] ) = = 0xbb & &
static_cast < unsigned char > ( str [ 2 ] ) = = 0xbf ) ;
2009-10-10 09:29:06 +02:00
}
2009-05-13 21:18:02 +02:00
2011-04-27 11:13:05 +02:00
// This wrapper exists because Sun's CC does not allow a static_cast
// from extern "C" int(*)(int) to int(*)(int).
static int tolowerWrapper ( int c )
{
return std : : tolower ( c ) ;
}
2011-02-20 11:56:52 +01:00
static bool isFallThroughComment ( std : : string comment )
{
// convert comment to lower case without whitespace
2011-10-13 20:53:06 +02:00
for ( std : : string : : iterator i = comment . begin ( ) ; i ! = comment . end ( ) ; ) {
2011-10-16 07:52:54 +02:00
if ( std : : isspace ( static_cast < unsigned char > ( * i ) ) )
2011-03-07 22:00:30 +01:00
i = comment . erase ( i ) ;
2011-03-06 19:50:48 +01:00
else
+ + i ;
}
2012-01-02 12:58:34 +01:00
std : : transform ( comment . begin ( ) , comment . end ( ) , comment . begin ( ) , tolowerWrapper ) ;
2011-02-20 11:56:52 +01:00
return comment . find ( " fallthr " ) ! = std : : string : : npos | |
2011-03-05 06:02:38 +01:00
comment . find ( " fallsthr " ) ! = std : : string : : npos | |
comment . find ( " fall-thr " ) ! = std : : string : : npos | |
2011-02-20 11:56:52 +01:00
comment . find ( " dropthr " ) ! = std : : string : : npos | |
comment . find ( " passthr " ) ! = std : : string : : npos | |
2011-03-05 06:02:38 +01:00
comment . find ( " nobreak " ) ! = std : : string : : npos | |
comment = = " fall " ;
2011-02-20 11:56:52 +01:00
}
2012-01-06 08:09:53 +01:00
std : : string Preprocessor : : removeComments ( const std : : string & str , const std : : string & filename )
2009-05-13 21:18:02 +02:00
{
// For the error report
2010-08-06 19:38:21 +02:00
unsigned int lineno = 1 ;
2009-05-13 21:18:02 +02:00
2011-11-13 20:27:56 +01:00
// handling <backslash><newline>
// when this is encountered the <backslash><newline> will be "skipped".
2009-05-13 21:18:02 +02:00
// on the next <newline>, extra newlines will be added
unsigned int newlines = 0 ;
std : : ostringstream code ;
2009-12-20 09:35:51 +01:00
unsigned char previous = 0 ;
2011-03-04 07:26:48 +01:00
bool inPreprocessorLine = false ;
2009-12-06 19:38:53 +01:00
std : : vector < std : : string > suppressionIDs ;
2011-03-06 00:14:10 +01:00
bool fallThroughComment = false ;
2009-12-06 19:38:53 +01:00
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type i = hasbom ( str ) ? 3U : 0U ; i < str . length ( ) ; + + i ) {
2010-08-06 19:38:21 +02:00
unsigned char ch = static_cast < unsigned char > ( str [ i ] ) ;
2011-10-13 20:53:06 +02:00
if ( ch & 0x80 ) {
2010-02-27 17:06:33 +01:00
std : : ostringstream errmsg ;
errmsg < < " The code contains characters that are unhandled. "
2010-12-15 18:45:53 +01:00
< < " Neither unicode nor extended ASCII are supported. "
2010-04-15 20:08:51 +02:00
< < " (line= " < < lineno < < " , character code= " < < std : : hex < < ( int ( ch ) & 0xff ) < < " ) " ;
2010-07-23 13:54:52 +02:00
writeError ( filename , lineno , _errorLogger , " syntaxError " , errmsg . str ( ) ) ;
2014-06-01 11:24:10 +02:00
_foundUnhandledChars = true ;
2010-02-27 13:08:59 +01:00
}
2009-05-13 21:18:02 +02:00
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2012-09-12 16:10:45 +02:00
if ( ( str . compare ( i , 7 , " #error " ) = = 0 & & ( ! _settings | | _settings - > userDefines . empty ( ) ) ) | |
str . compare ( i , 9 , " #warning " ) = = 0 ) {
2010-04-02 07:30:58 +02:00
if ( str . compare ( i , 6 , " #error " ) = = 0 )
2009-12-22 20:38:12 +01:00
code < < " #error " ;
i = str . find ( " \n " , i ) ;
2010-04-02 07:30:58 +02:00
if ( i = = std : : string : : npos )
2009-12-22 20:38:12 +01:00
break ;
- - i ;
continue ;
}
2011-02-19 23:36:03 +01:00
// First skip over any whitespace that may be present
2011-10-13 20:53:06 +02:00
if ( std : : isspace ( ch ) ) {
if ( ch = = ' ' & & previous = = ' ' ) {
2011-02-19 23:36:03 +01:00
// Skip double white space
2011-10-13 20:53:06 +02:00
} else {
2011-02-19 23:36:03 +01:00
code < < char ( ch ) ;
previous = ch ;
}
2011-11-13 20:27:56 +01:00
// if there has been <backslash><newline> sequences, add extra newlines..
2011-10-13 20:53:06 +02:00
if ( ch = = ' \n ' ) {
2011-03-04 07:26:48 +01:00
if ( previous ! = ' \\ ' )
inPreprocessorLine = false ;
2011-02-19 23:36:03 +01:00
+ + lineno ;
2011-10-13 20:53:06 +02:00
if ( newlines > 0 ) {
2011-02-19 23:36:03 +01:00
code < < std : : string ( newlines , ' \n ' ) ;
newlines = 0 ;
previous = ' \n ' ;
2010-07-23 23:12:56 +02:00
}
}
2011-02-19 23:36:03 +01:00
continue ;
2009-12-06 19:38:53 +01:00
}
2009-01-24 20:28:30 +01:00
// Remove comments..
2014-03-09 14:26:16 +01:00
if ( str . compare ( i , 2 , " // " ) = = 0 ) {
2012-07-08 23:39:46 +02:00
std : : size_t commentStart = i + 2 ;
2009-05-14 21:53:49 +02:00
i = str . find ( ' \n ' , i ) ;
2010-04-02 07:30:58 +02:00
if ( i = = std : : string : : npos )
2009-05-14 21:53:49 +02:00
break ;
2011-02-19 09:33:29 +01:00
std : : string comment ( str , commentStart , i - commentStart ) ;
2009-01-24 20:28:30 +01:00
2012-01-06 08:09:53 +01:00
if ( _settings & & _settings - > _inlineSuppressions ) {
2009-12-06 19:38:53 +01:00
std : : istringstream iss ( comment ) ;
std : : string word ;
iss > > word ;
2011-10-13 20:53:06 +02:00
if ( word = = " cppcheck-suppress " ) {
2009-12-06 19:38:53 +01:00
iss > > word ;
2010-04-02 07:30:58 +02:00
if ( iss )
2009-12-06 19:38:53 +01:00
suppressionIDs . push_back ( word ) ;
}
}
2011-10-13 20:53:06 +02:00
if ( isFallThroughComment ( comment ) ) {
2011-03-06 00:14:10 +01:00
fallThroughComment = true ;
2011-02-19 09:33:29 +01:00
}
2009-05-14 21:53:49 +02:00
code < < " \n " ;
previous = ' \n ' ;
+ + lineno ;
2014-03-09 14:26:16 +01:00
} else if ( str . compare ( i , 2 , " /* " ) = = 0 ) {
2012-07-08 23:39:46 +02:00
std : : size_t commentStart = i + 2 ;
2009-12-20 09:35:51 +01:00
unsigned char chPrev = 0 ;
2009-05-14 21:53:49 +02:00
+ + i ;
2011-10-13 20:53:06 +02:00
while ( i < str . length ( ) & & ( chPrev ! = ' * ' | | ch ! = ' / ' ) ) {
2009-05-14 21:53:49 +02:00
chPrev = ch ;
+ + i ;
2010-08-06 19:38:21 +02:00
ch = static_cast < unsigned char > ( str [ i ] ) ;
2011-10-13 20:53:06 +02:00
if ( ch = = ' \n ' ) {
2009-08-19 23:27:47 +02:00
+ + newlines ;
2009-01-24 20:28:30 +01:00
+ + lineno ;
2009-05-14 21:53:49 +02:00
}
2009-01-24 20:28:30 +01:00
}
2011-03-05 06:02:38 +01:00
std : : string comment ( str , commentStart , i - commentStart - 1 ) ;
2011-02-19 21:54:01 +01:00
2011-10-13 20:53:06 +02:00
if ( isFallThroughComment ( comment ) ) {
2011-03-06 00:14:10 +01:00
fallThroughComment = true ;
2011-02-19 21:54:01 +01:00
}
2009-01-24 20:28:30 +01:00
2012-01-06 08:09:53 +01:00
if ( _settings & & _settings - > _inlineSuppressions ) {
2011-02-19 23:36:03 +01:00
std : : istringstream iss ( comment ) ;
std : : string word ;
iss > > word ;
2011-10-13 20:53:06 +02:00
if ( word = = " cppcheck-suppress " ) {
2011-02-19 23:36:03 +01:00
iss > > word ;
if ( iss )
suppressionIDs . push_back ( word ) ;
2009-05-14 21:53:49 +02:00
}
2009-01-24 20:28:30 +01:00
}
2014-03-18 21:41:47 +01:00
} else if ( ( i = = 0 | | std : : isspace ( ( unsigned char ) str [ i - 1 ] ) ) & & str . compare ( i , 5 , " __asm " ) = = 0 ) {
while ( i < str . size ( ) & & ( std : : isalpha ( ( unsigned char ) str [ i ] ) | | str [ i ] = = ' _ ' ) )
2012-09-30 09:35:32 +02:00
code < < str [ i + + ] ;
2014-03-18 21:41:47 +01:00
while ( i < str . size ( ) & & std : : isspace ( ( unsigned char ) str [ i ] ) )
2012-09-30 09:35:32 +02:00
code < < str [ i + + ] ;
if ( str [ i ] = = ' { ' ) {
2013-06-25 06:45:11 +02:00
// Ticket 4873: Extract comments from the __asm / __asm__'s content
std : : string asmBody ;
2012-09-30 09:35:32 +02:00
while ( i < str . size ( ) & & str [ i ] ! = ' } ' ) {
2013-08-04 14:34:28 +02:00
if ( str [ i ] = = ' ; ' ) {
std : : string : : size_type backslashN = str . find ( " \n " , i ) ;
if ( backslashN ! = std : : string : : npos ) // Ticket #4922: Don't go in infinite loop or crash if there is no '\n'
i = backslashN ;
}
2013-06-25 06:45:11 +02:00
asmBody + = str [ i + + ] ;
2012-09-30 09:35:32 +02:00
}
2013-06-25 06:45:11 +02:00
code < < removeComments ( asmBody , filename ) ;
2012-09-30 09:35:32 +02:00
code < < ' } ' ;
2012-11-04 12:21:34 +01:00
} else
- - i ;
2011-10-13 20:53:06 +02:00
} else if ( ch = = ' # ' & & previous = = ' \n ' ) {
2011-03-04 07:26:48 +01:00
code < < ch ;
previous = ch ;
inPreprocessorLine = true ;
2011-04-22 20:25:17 +02:00
// Add any pending inline suppressions that have accumulated.
2011-10-13 20:53:06 +02:00
if ( ! suppressionIDs . empty ( ) ) {
2014-02-15 08:37:57 +01:00
if ( _settings ! = nullptr ) {
2011-04-22 20:25:17 +02:00
// Add the suppressions.
2012-07-08 23:39:46 +02:00
for ( std : : size_t j = 0 ; j < suppressionIDs . size ( ) ; + + j ) {
2012-01-06 08:09:53 +01:00
const std : : string errmsg ( _settings - > nomsg . addSuppression ( suppressionIDs [ j ] , filename , lineno ) ) ;
2011-10-13 20:53:06 +02:00
if ( ! errmsg . empty ( ) ) {
2011-04-22 20:25:17 +02:00
writeError ( filename , lineno , _errorLogger , " cppcheckError " , errmsg ) ;
}
}
}
suppressionIDs . clear ( ) ;
}
2011-10-13 20:53:06 +02:00
} else {
if ( ! inPreprocessorLine ) {
2011-03-04 07:26:48 +01:00
// Not whitespace, not a comment, and not preprocessor.
// Must be code here!
2011-03-06 00:14:10 +01:00
// First check for a "fall through" comment match, but only
// add a suppression if the next token is 'case' or 'default'
2012-01-06 08:01:50 +01:00
if ( _settings & & _settings - > isEnabled ( " style " ) & & _settings - > experimental & & fallThroughComment ) {
2011-03-06 00:14:10 +01:00
std : : string : : size_type j = str . find_first_not_of ( " abcdefghijklmnopqrstuvwxyz " , i ) ;
std : : string tok = str . substr ( i , j - i ) ;
if ( tok = = " case " | | tok = = " default " )
suppressionIDs . push_back ( " switchCaseFallThrough " ) ;
fallThroughComment = false ;
}
2011-03-04 07:26:48 +01:00
// Add any pending inline suppressions that have accumulated.
2011-10-13 20:53:06 +02:00
if ( ! suppressionIDs . empty ( ) ) {
2014-02-15 08:37:57 +01:00
if ( _settings ! = nullptr ) {
2013-08-28 06:46:32 +02:00
// Relative filename
std : : string relativeFilename ( filename ) ;
if ( _settings - > _relativePaths ) {
for ( std : : size_t j = 0U ; j < _settings - > _basePaths . size ( ) ; + + j ) {
const std : : string bp = _settings - > _basePaths [ j ] + " / " ;
if ( relativeFilename . compare ( 0 , bp . size ( ) , bp ) = = 0 ) {
relativeFilename = relativeFilename . substr ( bp . size ( ) ) ;
}
}
}
2011-03-04 07:26:48 +01:00
// Add the suppressions.
2012-07-08 23:39:46 +02:00
for ( std : : size_t j = 0 ; j < suppressionIDs . size ( ) ; + + j ) {
2013-08-28 06:46:32 +02:00
const std : : string errmsg ( _settings - > nomsg . addSuppression ( suppressionIDs [ j ] , relativeFilename , lineno ) ) ;
2011-10-13 20:53:06 +02:00
if ( ! errmsg . empty ( ) ) {
2011-03-04 07:26:48 +01:00
writeError ( filename , lineno , _errorLogger , " cppcheckError " , errmsg ) ;
}
2011-02-19 23:36:03 +01:00
}
}
2011-03-04 07:26:48 +01:00
suppressionIDs . clear ( ) ;
2010-09-13 19:36:40 +02:00
}
}
2011-02-19 23:36:03 +01:00
// String or char constants..
2011-10-13 20:53:06 +02:00
if ( ch = = ' \" ' | | ch = = ' \' ' ) {
2011-02-19 23:36:03 +01:00
code < < char ( ch ) ;
char chNext ;
2011-10-13 20:53:06 +02:00
do {
2011-02-19 23:36:03 +01:00
+ + i ;
chNext = str [ i ] ;
2011-10-13 20:53:06 +02:00
if ( chNext = = ' \\ ' ) {
2011-02-19 23:36:03 +01:00
+ + i ;
char chSeq = str [ i ] ;
if ( chSeq = = ' \n ' )
+ + newlines ;
2011-10-13 20:53:06 +02:00
else {
2011-02-19 23:36:03 +01:00
code < < chNext ;
code < < chSeq ;
previous = static_cast < unsigned char > ( chSeq ) ;
}
2011-10-13 20:53:06 +02:00
} else {
2011-02-19 23:36:03 +01:00
code < < chNext ;
previous = static_cast < unsigned char > ( chNext ) ;
2010-09-13 19:36:40 +02:00
}
2011-10-13 20:53:06 +02:00
} while ( i < str . length ( ) & & chNext ! = ch & & chNext ! = ' \n ' ) ;
2010-09-13 19:36:40 +02:00
}
2009-01-24 20:28:30 +01:00
2011-02-19 23:36:03 +01:00
// Rawstring..
2011-10-13 20:53:06 +02:00
else if ( str . compare ( i , 2 , " R \" " ) = = 0 ) {
2011-02-19 23:36:03 +01:00
std : : string delim ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type i2 = i + 2 ; i2 < str . length ( ) ; + + i2 ) {
2011-02-19 23:36:03 +01:00
if ( i2 > 16 | |
std : : isspace ( str [ i2 ] ) | |
std : : iscntrl ( str [ i2 ] ) | |
str [ i2 ] = = ' ) ' | |
2011-10-13 20:53:06 +02:00
str [ i2 ] = = ' \\ ' ) {
2011-02-19 23:36:03 +01:00
delim = " " ;
break ;
2011-10-13 20:53:06 +02:00
} else if ( str [ i2 ] = = ' ( ' )
2011-02-19 23:36:03 +01:00
break ;
delim + = str [ i2 ] ;
}
const std : : string : : size_type endpos = str . find ( " ) " + delim + " \" " , i ) ;
2011-10-13 20:53:06 +02:00
if ( delim ! = " " & & endpos ! = std : : string : : npos ) {
2011-02-19 23:36:03 +01:00
unsigned int rawstringnewlines = 0 ;
code < < ' \" ' ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type p = i + 3 + delim . size ( ) ; p < endpos ; + + p ) {
if ( str [ p ] = = ' \n ' ) {
2011-02-19 23:36:03 +01:00
rawstringnewlines + + ;
code < < " \\ n " ;
2011-10-13 20:53:06 +02:00
} else if ( std : : iscntrl ( ( unsigned char ) str [ p ] ) | |
std : : isspace ( ( unsigned char ) str [ p ] ) ) {
2011-02-19 23:36:03 +01:00
code < < " " ;
2011-10-13 20:53:06 +02:00
} else if ( str [ p ] = = ' \" ' | | str [ p ] = = ' \' ' ) {
2011-02-19 23:36:03 +01:00
code < < " \\ " < < ( char ) str [ p ] ;
2011-10-13 20:53:06 +02:00
} else {
2011-02-19 23:36:03 +01:00
code < < ( char ) str [ p ] ;
}
}
code < < " \" " ;
if ( rawstringnewlines > 0 )
code < < std : : string ( rawstringnewlines , ' \n ' ) ;
2012-11-30 13:46:46 +01:00
i = endpos + delim . size ( ) + 1 ;
2011-10-13 20:53:06 +02:00
} else {
2011-02-19 23:36:03 +01:00
code < < " R " ;
previous = ' R ' ;
}
2011-10-13 20:53:06 +02:00
} else {
2010-08-06 19:38:21 +02:00
code < < char ( ch ) ;
2009-05-14 21:53:49 +02:00
previous = ch ;
}
2009-01-24 20:28:30 +01:00
}
}
return code . str ( ) ;
}
2011-02-26 19:04:38 +01:00
std : : string Preprocessor : : removeIf0 ( const std : : string & code )
{
std : : ostringstream ret ;
std : : istringstream istr ( code ) ;
std : : string line ;
2011-10-13 20:53:06 +02:00
while ( std : : getline ( istr , line ) ) {
2012-01-02 15:52:19 +01:00
ret < < line < < " \n " ;
if ( line = = " #if 0 " ) {
2011-02-26 19:04:38 +01:00
// goto the end of the '#if 0' block
unsigned int level = 1 ;
2011-03-01 08:04:11 +01:00
bool in = false ;
2011-10-13 20:53:06 +02:00
while ( level > 0 & & std : : getline ( istr , line ) ) {
2011-02-26 19:04:38 +01:00
if ( line . compare ( 0 , 3 , " #if " ) = = 0 )
+ + level ;
else if ( line = = " #endif " )
- - level ;
2011-10-13 20:53:06 +02:00
else if ( ( line = = " #else " ) | | ( line . compare ( 0 , 5 , " #elif " ) = = 0 ) ) {
2011-03-01 08:04:11 +01:00
if ( level = = 1 )
in = true ;
2011-10-13 20:53:06 +02:00
} else {
2011-03-01 08:04:11 +01:00
if ( in )
ret < < line < < " \n " ;
else
// replace code within '#if 0' block with empty lines
ret < < " \n " ;
continue ;
}
2011-02-26 20:09:14 +01:00
2011-03-01 08:04:11 +01:00
ret < < line < < " \n " ;
2011-02-26 19:04:38 +01:00
}
}
}
return ret . str ( ) ;
}
2009-09-11 23:34:24 +02:00
2011-03-30 16:45:31 +02:00
std : : string Preprocessor : : removeParentheses ( const std : : string & str )
2009-10-04 07:25:30 +02:00
{
2010-04-02 07:30:58 +02:00
if ( str . find ( " \n #if " ) = = std : : string : : npos & & str . compare ( 0 , 3 , " #if " ) ! = 0 )
2009-10-04 07:25:30 +02:00
return str ;
2011-12-08 21:28:34 +01:00
std : : istringstream istr ( str ) ;
2009-10-04 07:25:30 +02:00
std : : ostringstream ret ;
std : : string line ;
2011-10-13 20:53:06 +02:00
while ( std : : getline ( istr , line ) ) {
if ( line . compare ( 0 , 3 , " #if " ) = = 0 | | line . compare ( 0 , 5 , " #elif " ) = = 0 ) {
2009-10-04 15:41:50 +02:00
std : : string : : size_type pos ;
pos = 0 ;
2010-04-02 07:30:58 +02:00
while ( ( pos = line . find ( " ( " , pos ) ) ! = std : : string : : npos )
2009-10-04 15:41:50 +02:00
line . erase ( pos , 1 ) ;
pos = 0 ;
2010-04-02 07:30:58 +02:00
while ( ( pos = line . find ( " ( " , pos ) ) ! = std : : string : : npos )
2009-10-04 15:41:50 +02:00
line . erase ( pos + 1 , 1 ) ;
pos = 0 ;
2010-04-02 07:30:58 +02:00
while ( ( pos = line . find ( " ) " , pos ) ) ! = std : : string : : npos )
2009-10-04 15:41:50 +02:00
line . erase ( pos , 1 ) ;
pos = 0 ;
2010-04-02 07:30:58 +02:00
while ( ( pos = line . find ( " ) " , pos ) ) ! = std : : string : : npos )
2009-10-04 15:41:50 +02:00
line . erase ( pos + 1 , 1 ) ;
2013-01-16 15:37:07 +01:00
// Remove inner parentheses "((..))"..
2009-10-04 15:41:50 +02:00
pos = 0 ;
2011-10-13 20:53:06 +02:00
while ( ( pos = line . find ( " (( " , pos ) ) ! = std : : string : : npos ) {
2009-10-04 15:41:50 +02:00
+ + pos ;
std : : string : : size_type pos2 = line . find_first_of ( " () " , pos + 1 ) ;
2011-10-13 20:53:06 +02:00
if ( pos2 ! = std : : string : : npos & & line [ pos2 ] = = ' ) ' ) {
2009-10-04 15:41:50 +02:00
line . erase ( pos2 , 1 ) ;
line . erase ( pos , 1 ) ;
}
}
2009-10-06 10:47:36 +02:00
// "#if(A) => #if A", but avoid "#if (defined A) || defined (B)"
2010-04-02 07:30:58 +02:00
if ( ( line . compare ( 0 , 4 , " #if( " ) = = 0 | | line . compare ( 0 , 6 , " #elif( " ) = = 0 ) & &
2011-10-13 20:53:06 +02:00
line [ line . length ( ) - 1 ] = = ' ) ' ) {
2009-10-06 10:47:36 +02:00
int ind = 0 ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type i = 0 ; i < line . length ( ) ; + + i ) {
2010-04-02 07:30:58 +02:00
if ( line [ i ] = = ' ( ' )
2009-10-06 10:47:36 +02:00
+ + ind ;
2011-10-13 20:53:06 +02:00
else if ( line [ i ] = = ' ) ' ) {
2009-10-06 10:47:36 +02:00
- - ind ;
2011-10-13 20:53:06 +02:00
if ( ind = = 0 ) {
if ( i = = line . length ( ) - 1 ) {
2013-12-15 13:41:07 +01:00
line [ line . find ( ' ( ' ) ] = ' ' ;
line . erase ( line . length ( ) - 1 ) ;
2009-10-06 10:47:36 +02:00
}
break ;
}
}
}
2009-10-04 07:25:30 +02:00
}
2009-10-06 10:47:36 +02:00
2010-04-02 07:30:58 +02:00
if ( line . compare ( 0 , 4 , " #if( " ) = = 0 )
2009-10-06 10:47:36 +02:00
line . insert ( 3 , " " ) ;
2010-04-02 07:30:58 +02:00
else if ( line . compare ( 0 , 6 , " #elif( " ) = = 0 )
2009-10-06 10:47:36 +02:00
line . insert ( 5 , " " ) ;
2009-10-04 07:25:30 +02:00
}
ret < < line < < " \n " ;
}
2009-10-06 10:47:36 +02:00
2009-10-04 07:25:30 +02:00
return ret . str ( ) ;
}
2009-09-11 23:34:24 +02:00
void Preprocessor : : removeAsm ( std : : string & str )
{
std : : string : : size_type pos = 0 ;
2011-10-13 20:53:06 +02:00
while ( ( pos = str . find ( " #asm \n " , pos ) ) ! = std : : string : : npos ) {
2012-01-02 12:58:34 +01:00
str . replace ( pos , 4 , " asm( " ) ;
2011-05-11 19:27:19 +02:00
2012-01-02 12:58:34 +01:00
std : : string : : size_type pos2 = str . find ( " #endasm " , pos ) ;
if ( pos2 ! = std : : string : : npos ) {
str . replace ( pos2 , 7 , " ); " ) ;
pos = pos2 ;
2011-05-11 19:27:19 +02:00
}
}
2009-09-11 21:22:41 +02:00
}
2009-07-25 21:10:30 +02:00
void Preprocessor : : preprocess ( std : : istream & istr , std : : map < std : : string , std : : string > & result , const std : : string & filename , const std : : list < std : : string > & includePaths )
2009-01-24 20:28:30 +01:00
{
std : : list < std : : string > configs ;
std : : string data ;
preprocess ( istr , data , configs , filename , includePaths ) ;
2011-11-30 20:24:01 +01:00
for ( std : : list < std : : string > : : const_iterator it = configs . begin ( ) ; it ! = configs . end ( ) ; + + it ) {
2013-06-08 16:46:54 +02:00
if ( _settings & & ( _settings - > userUndefs . find ( * it ) = = _settings - > userUndefs . end ( ) ) ) {
2012-01-06 08:01:50 +01:00
result [ * it ] = getcode ( data , * it , filename ) ;
2013-06-08 16:46:54 +02:00
}
2011-11-30 20:24:01 +01:00
}
2009-01-24 20:28:30 +01:00
}
std : : string Preprocessor : : removeSpaceNearNL ( const std : : string & str )
{
std : : string tmp ;
2010-08-06 19:38:21 +02:00
char prev = 0 ;
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < str . size ( ) ; i + + ) {
2010-04-02 07:30:58 +02:00
if ( str [ i ] = = ' ' & &
2010-08-06 19:38:21 +02:00
( ( i > 0 & & prev = = ' \n ' ) | |
2010-04-02 07:30:58 +02:00
( i + 1 < str . size ( ) & & str [ i + 1 ] = = ' \n ' )
)
2011-10-13 20:53:06 +02:00
) {
2009-01-24 20:28:30 +01:00
// Ignore space that has new line in either side of it
2011-10-13 20:53:06 +02:00
} else {
2009-01-24 20:28:30 +01:00
tmp . append ( 1 , str [ i ] ) ;
2010-08-06 19:38:21 +02:00
prev = str [ i ] ;
2009-01-24 20:28:30 +01:00
}
}
return tmp ;
}
2013-07-24 13:06:59 +02:00
std : : string Preprocessor : : replaceIfDefined ( const std : : string & str ) const
2009-01-24 20:28:30 +01:00
{
std : : string ret ( str ) ;
2009-06-21 08:03:42 +02:00
std : : string : : size_type pos ;
pos = 0 ;
2011-10-13 20:53:06 +02:00
while ( ( pos = ret . find ( " #if defined( " , pos ) ) ! = std : : string : : npos ) {
2009-01-24 20:28:30 +01:00
std : : string : : size_type pos2 = ret . find ( " ) " , pos + 9 ) ;
2010-04-02 07:30:58 +02:00
if ( pos2 > ret . length ( ) - 1 )
2009-01-24 20:28:30 +01:00
break ;
2011-10-13 20:53:06 +02:00
if ( ret [ pos2 + 1 ] = = ' \n ' ) {
2009-01-24 20:28:30 +01:00
ret . erase ( pos2 , 1 ) ;
ret . erase ( pos + 3 , 9 ) ;
ret . insert ( pos + 3 , " def " ) ;
}
+ + pos ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2009-01-24 20:28:30 +01:00
}
2009-06-21 08:03:42 +02:00
pos = 0 ;
2011-10-13 20:53:06 +02:00
while ( ( pos = ret . find ( " #if !defined( " , pos ) ) ! = std : : string : : npos ) {
2009-06-21 08:03:42 +02:00
std : : string : : size_type pos2 = ret . find ( " ) " , pos + 9 ) ;
2010-04-02 07:30:58 +02:00
if ( pos2 > ret . length ( ) - 1 )
2009-06-21 08:03:42 +02:00
break ;
2011-10-13 20:53:06 +02:00
if ( ret [ pos2 + 1 ] = = ' \n ' ) {
2009-06-21 08:03:42 +02:00
ret . erase ( pos2 , 1 ) ;
ret . erase ( pos + 3 , 10 ) ;
ret . insert ( pos + 3 , " ndef " ) ;
}
+ + pos ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2009-06-21 08:03:42 +02:00
}
2009-10-04 15:41:50 +02:00
pos = 0 ;
2011-10-13 20:53:06 +02:00
while ( ( pos = ret . find ( " #elif defined( " , pos ) ) ! = std : : string : : npos ) {
2009-10-04 15:41:50 +02:00
std : : string : : size_type pos2 = ret . find ( " ) " , pos + 9 ) ;
2010-04-02 07:30:58 +02:00
if ( pos2 > ret . length ( ) - 1 )
2009-10-04 15:41:50 +02:00
break ;
2011-10-13 20:53:06 +02:00
if ( ret [ pos2 + 1 ] = = ' \n ' ) {
2009-10-04 15:41:50 +02:00
ret . erase ( pos2 , 1 ) ;
ret . erase ( pos + 6 , 8 ) ;
}
+ + pos ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2009-10-04 15:41:50 +02:00
}
2009-01-24 20:28:30 +01:00
return ret ;
}
2010-04-15 22:45:38 +02:00
void Preprocessor : : preprocessWhitespaces ( std : : string & processedFile )
2009-01-24 20:28:30 +01:00
{
// Replace all tabs with spaces..
std : : replace ( processedFile . begin ( ) , processedFile . end ( ) , ' \t ' , ' ' ) ;
// Remove all indentation..
2010-04-02 07:30:58 +02:00
if ( ! processedFile . empty ( ) & & processedFile [ 0 ] = = ' ' )
2009-01-24 20:28:30 +01:00
processedFile . erase ( 0 , processedFile . find_first_not_of ( " " ) ) ;
// Remove space characters that are after or before new line character
processedFile = removeSpaceNearNL ( processedFile ) ;
2010-04-15 22:45:38 +02:00
}
void Preprocessor : : preprocess ( std : : istream & srcCodeStream , std : : string & processedFile , std : : list < std : : string > & resultConfigurations , const std : : string & filename , const std : : list < std : : string > & includePaths )
{
2012-12-27 16:52:31 +01:00
std : : string forcedIncludes ;
2010-09-03 13:30:49 +02:00
if ( file0 . empty ( ) )
file0 = filename ;
2012-01-05 18:37:15 +01:00
processedFile = read ( srcCodeStream , filename ) ;
2010-04-15 22:45:38 +02:00
2013-06-12 16:59:25 +02:00
if ( _settings ) {
2012-12-27 16:52:31 +01:00
for ( std : : list < std : : string > : : iterator it = _settings - > userIncludes . begin ( ) ;
2012-12-27 16:59:30 +01:00
it ! = _settings - > userIncludes . end ( ) ;
2012-12-27 17:11:22 +01:00
+ + it ) {
2012-12-27 16:52:31 +01:00
std : : string cur = * it ;
// try to open file
std : : ifstream fin ;
fin . open ( cur . c_str ( ) ) ;
if ( ! fin . is_open ( ) ) {
2013-03-13 06:48:33 +01:00
missingInclude ( cur ,
1 ,
cur ,
UserHeader
) ;
2012-12-27 16:52:31 +01:00
continue ;
}
2013-06-12 16:59:25 +02:00
const std : : string fileData = read ( fin , filename ) ;
2012-12-27 16:52:31 +01:00
fin . close ( ) ;
forcedIncludes =
2012-12-27 16:59:30 +01:00
forcedIncludes +
" #file \" " + cur + " \" \n " +
" #line 1 \n " +
fileData + " \n " +
" #endfile \n "
;
2012-12-27 16:52:31 +01:00
}
2014-02-08 08:51:38 +01:00
2014-02-06 09:22:07 +01:00
for ( std : : vector < std : : string > : : iterator it = _settings - > library . defines . begin ( ) ;
it ! = _settings - > library . defines . end ( ) ;
2014-02-08 08:51:38 +01:00
+ + it ) {
2014-02-06 09:22:07 +01:00
forcedIncludes + = * it ;
}
2012-12-27 16:52:31 +01:00
}
if ( ! forcedIncludes . empty ( ) ) {
processedFile =
2012-12-27 16:59:30 +01:00
forcedIncludes +
" #file \" " + filename + " \" \n " +
" #line 1 \n " +
processedFile +
" #endfile \n "
;
2012-12-27 16:52:31 +01:00
}
2009-09-11 21:22:41 +02:00
// Remove asm(...)
removeAsm ( processedFile ) ;
2009-07-22 20:11:27 +02:00
// Replace "defined A" with "defined(A)"
{
2011-12-08 21:28:34 +01:00
std : : istringstream istr ( processedFile ) ;
2009-07-22 20:11:27 +02:00
std : : ostringstream ostr ;
std : : string line ;
2011-10-13 20:53:06 +02:00
while ( std : : getline ( istr , line ) ) {
if ( line . compare ( 0 , 4 , " #if " ) = = 0 | | line . compare ( 0 , 6 , " #elif " ) = = 0 ) {
2009-07-22 20:11:27 +02:00
std : : string : : size_type pos = 0 ;
2011-10-13 20:53:06 +02:00
while ( ( pos = line . find ( " defined " ) ) ! = std : : string : : npos ) {
2009-07-22 20:11:27 +02:00
line [ pos + 8 ] = ' ( ' ;
pos = line . find_first_of ( " |& " , pos + 8 ) ;
2010-04-02 07:30:58 +02:00
if ( pos = = std : : string : : npos )
2009-07-22 20:11:27 +02:00
line + = " ) " ;
else
line . insert ( pos , " ) " ) ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return ;
2009-07-22 20:11:27 +02:00
}
}
ostr < < line < < " \n " ;
}
processedFile = ostr . str ( ) ;
}
2013-11-15 19:21:21 +01:00
std : : map < std : : string , std : : string > defs ( getcfgmap ( _settings ? _settings - > userDefines : std : : string ( " " ) , _settings , filename ) ) ;
2011-10-24 20:59:46 +02:00
2013-06-08 16:46:54 +02:00
if ( _settings & & _settings - > _maxConfigs = = 1U ) {
2013-10-27 10:33:37 +01:00
std : : set < std : : string > pragmaOnce ;
2013-08-18 18:04:06 +02:00
std : : list < std : : string > includes ;
processedFile = handleIncludes ( processedFile , filename , includePaths , defs , pragmaOnce , includes ) ;
2013-06-08 16:46:54 +02:00
resultConfigurations = getcfgs ( processedFile , filename , defs ) ;
2011-10-24 20:59:46 +02:00
} else {
handleIncludes ( processedFile , filename , includePaths ) ;
2009-01-24 20:28:30 +01:00
2011-10-24 20:59:46 +02:00
processedFile = replaceIfDefined ( processedFile ) ;
2009-01-24 20:28:30 +01:00
2011-10-24 20:59:46 +02:00
// Get all possible configurations..
2013-06-08 16:46:54 +02:00
resultConfigurations = getcfgs ( processedFile , filename , defs ) ;
2012-07-18 20:57:00 +02:00
// Remove configurations that are disabled by -U
handleUndef ( resultConfigurations ) ;
2011-10-24 20:59:46 +02:00
}
2009-01-24 20:28:30 +01:00
}
2012-07-18 20:57:00 +02:00
void Preprocessor : : handleUndef ( std : : list < std : : string > & configurations ) const
{
if ( _settings & & ! _settings - > userUndefs . empty ( ) ) {
for ( std : : list < std : : string > : : iterator cfg = configurations . begin ( ) ; cfg ! = configurations . end ( ) ; ) {
bool undef = false ;
for ( std : : set < std : : string > : : const_iterator it = _settings - > userUndefs . begin ( ) ; it ! = _settings - > userUndefs . end ( ) ; + + it ) {
if ( * it = = * cfg )
undef = true ;
else if ( cfg - > compare ( 0 , it - > length ( ) , * it ) = = 0 & & cfg - > find_first_of ( " ; = " ) == it->length())
undef = true ;
else if ( cfg - > find ( " ; " + * it ) = = std : : string : : npos )
;
else if ( cfg - > find ( " ; " + * it + " ; " ) ! = std : : string : : npos )
undef = true ;
else if ( cfg - > find ( " ; " + * it + " = " ) ! = std : : string : : npos )
undef = true ;
else if ( cfg - > find ( " ; " + * it ) + it - > size ( ) + 1U = = cfg - > size ( ) )
undef = true ;
}
2009-01-24 20:28:30 +01:00
2012-07-18 20:57:00 +02:00
if ( undef )
configurations . erase ( cfg + + ) ;
else
2012-07-24 21:21:05 +02:00
+ + cfg ;
2012-07-18 20:57:00 +02:00
}
}
}
2009-01-24 20:28:30 +01:00
// Get the DEF in this line: "#ifdef DEF"
std : : string Preprocessor : : getdef ( std : : string line , bool def )
{
2011-01-31 01:47:49 +01:00
if ( line . empty ( ) | | line [ 0 ] ! = ' # ' )
2011-01-31 03:37:37 +01:00
return " " ;
2011-01-31 01:47:49 +01:00
2009-01-24 20:28:30 +01:00
// If def is true, the line must start with "#ifdef"
2011-01-31 01:40:59 +01:00
if ( def & & line . compare ( 0 , 7 , " #ifdef " ) ! = 0 & & line . compare ( 0 , 4 , " #if " ) ! = 0
2011-10-13 20:53:06 +02:00
& & ( line . compare ( 0 , 6 , " #elif " ) ! = 0 | | line . compare ( 0 , 7 , " #elif ! " ) = = 0 ) ) {
2009-01-24 20:28:30 +01:00
return " " ;
}
// If def is false, the line must start with "#ifndef"
2011-10-13 20:53:06 +02:00
if ( ! def & & line . compare ( 0 , 8 , " #ifndef " ) ! = 0 & & line . compare ( 0 , 7 , " #elif ! " ) ! = 0 ) {
2009-01-24 20:28:30 +01:00
return " " ;
}
// Remove the "#ifdef" or "#ifndef"
2011-01-31 01:40:59 +01:00
if ( line . compare ( 0 , 12 , " #if defined " ) = = 0 )
2009-06-14 22:37:18 +02:00
line . erase ( 0 , 11 ) ;
2011-10-13 20:53:06 +02:00
else if ( line . compare ( 0 , 15 , " #elif !defined( " ) = = 0 ) {
2011-01-31 00:33:44 +01:00
line . erase ( 0 , 15 ) ;
2012-09-04 15:29:51 +02:00
std : : string : : size_type pos = line . find ( " ) " ) ;
2011-01-31 00:33:44 +01:00
// if pos == ::npos then another part of the code will complain
// about the mismatch
if ( pos ! = std : : string : : npos )
line . erase ( pos , 1 ) ;
2011-10-13 20:53:06 +02:00
} else
2009-06-14 22:37:18 +02:00
line . erase ( 0 , line . find ( " " ) ) ;
2009-01-24 20:28:30 +01:00
// Remove all spaces.
2009-12-11 19:28:37 +01:00
std : : string : : size_type pos = 0 ;
2011-10-13 20:53:06 +02:00
while ( ( pos = line . find ( " " , pos ) ) ! = std : : string : : npos ) {
2010-08-12 19:39:19 +02:00
const unsigned char chprev ( static_cast < unsigned char > ( ( pos > 0 ) ? line [ pos - 1 ] : 0 ) ) ;
const unsigned char chnext ( static_cast < unsigned char > ( ( pos + 1 < line . length ( ) ) ? line [ pos + 1 ] : 0 ) ) ;
2010-04-02 07:30:58 +02:00
if ( ( std : : isalnum ( chprev ) | | chprev = = ' _ ' ) & & ( std : : isalnum ( chnext ) | | chnext = = ' _ ' ) )
2009-12-11 19:28:37 +01:00
+ + pos ;
else
line . erase ( pos , 1 ) ;
}
2009-01-24 20:28:30 +01:00
// The remaining string is our result.
return line ;
}
2013-01-26 16:21:33 +01:00
/** Simplify variable in variable map. */
2013-01-27 02:53:29 +01:00
static Token * simplifyVarMapExpandValue ( Token * tok , const std : : map < std : : string , std : : string > & variables , std : : set < std : : string > seenVariables )
2013-01-26 16:21:33 +01:00
{
// TODO: handle function-macros too.
// Prevent infinite recursion..
if ( seenVariables . find ( tok - > str ( ) ) ! = seenVariables . end ( ) )
2013-01-27 02:53:29 +01:00
return tok ;
2013-01-26 16:21:33 +01:00
seenVariables . insert ( tok - > str ( ) ) ;
const std : : map < std : : string , std : : string > : : const_iterator it = variables . find ( tok - > str ( ) ) ;
if ( it ! = variables . end ( ) ) {
2014-02-15 08:37:57 +01:00
TokenList tokenList ( nullptr ) ;
2013-01-26 16:21:33 +01:00
std : : istringstream istr ( it - > second ) ;
if ( tokenList . createTokens ( istr ) ) {
// expand token list
for ( Token * tok2 = tokenList . front ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
2013-01-26 18:45:18 +01:00
if ( tok2 - > isName ( ) ) {
2013-11-01 11:14:50 +01:00
tok2 = simplifyVarMapExpandValue ( tok2 , variables , seenVariables ) ;
2013-01-26 16:21:33 +01:00
}
}
// insert token list into "parent" token list
for ( const Token * tok2 = tokenList . front ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > previous ( ) ) {
tok - > insertToken ( tok2 - > str ( ) ) ;
tok = tok - > next ( ) ;
} else
tok - > str ( tok2 - > str ( ) ) ;
}
}
}
2013-01-27 02:53:29 +01:00
return tok ;
2013-01-26 16:21:33 +01:00
}
2012-03-15 20:04:34 +01:00
/**
* Simplifies the variable map . For example if the map contains A = > B , B = > 1 , then A = > B is simplified to A = > 1.
* @ param [ in , out ] variables - a map of variable name to variable value . This map will be modified .
*/
static void simplifyVarMap ( std : : map < std : : string , std : : string > & variables )
{
for ( std : : map < std : : string , std : : string > : : iterator i = variables . begin ( ) ; i ! = variables . end ( ) ; + + i ) {
2014-02-15 08:37:57 +01:00
TokenList tokenList ( nullptr ) ;
2013-01-26 12:37:46 +01:00
std : : istringstream istr ( i - > second ) ;
if ( tokenList . createTokens ( istr ) ) {
for ( Token * tok = tokenList . front ( ) ; tok ; tok = tok - > next ( ) ) {
if ( tok - > isName ( ) ) {
2013-01-27 02:53:29 +01:00
std : : set < std : : string > seenVariables ;
tok = simplifyVarMapExpandValue ( tok , variables , seenVariables ) ;
2013-01-26 12:37:46 +01:00
}
2012-04-22 06:49:42 +02:00
}
2013-01-26 16:21:33 +01:00
std : : string str ;
for ( const Token * tok = tokenList . front ( ) ; tok ; tok = tok - > next ( ) )
str . append ( ( tok - > previous ( ) ? " " : " " ) + tok - > str ( ) ) ;
i - > second = str ;
2012-03-15 20:04:34 +01:00
}
}
}
2009-01-24 20:28:30 +01:00
2013-06-08 16:46:54 +02:00
std : : list < std : : string > Preprocessor : : getcfgs ( const std : : string & filedata , const std : : string & filename , const std : : map < std : : string , std : : string > & defs )
2009-01-24 20:28:30 +01:00
{
std : : list < std : : string > ret ;
ret . push_back ( " " ) ;
2010-03-19 16:13:45 +01:00
std : : list < std : : string > deflist , ndeflist ;
2009-01-24 20:28:30 +01:00
2009-07-22 18:47:50 +02:00
// constants defined through "#define" in the code..
std : : set < std : : string > defines ;
2013-09-04 12:19:31 +02:00
std : : map < std : : string , std : : string > alldefinesmap ( defs ) ;
2014-03-10 15:49:02 +01:00
std : : stack < std : : pair < std : : string , bool > > includeStack ;
includeStack . push ( std : : pair < std : : string , bool > ( filename , false ) ) ;
2009-07-22 18:47:50 +02:00
2009-01-24 20:28:30 +01:00
// How deep into included files are we currently parsing?
// 0=>Source file, 1=>Included by source file, 2=>included by header that was included by source file, etc
int filelevel = 0 ;
2010-02-06 09:30:48 +01:00
bool includeguard = false ;
2009-11-20 19:18:57 +01:00
unsigned int linenr = 0 ;
2009-01-24 20:28:30 +01:00
std : : istringstream istr ( filedata ) ;
std : : string line ;
2012-05-04 17:53:47 +02:00
while ( std : : getline ( istr , line ) ) {
2009-11-20 19:18:57 +01:00
+ + linenr ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return ret ;
2010-07-25 18:19:37 +02:00
if ( _errorLogger )
2010-08-03 16:36:21 +02:00
_errorLogger - > reportProgress ( filename , " Preprocessing (get configurations 1) " , 0 ) ;
2011-01-31 01:47:49 +01:00
if ( line . empty ( ) )
2011-01-31 03:37:37 +01:00
continue ;
2010-07-25 18:19:37 +02:00
2011-10-13 20:53:06 +02:00
if ( line . compare ( 0 , 6 , " #file " ) = = 0 ) {
2010-02-06 09:30:48 +01:00
includeguard = true ;
2014-03-10 15:49:02 +01:00
std : : string : : size_type start = line . find ( " \" " ) ;
std : : string : : size_type end = line . find ( " \" " , start + 1 ) ;
std : : string includeFile = line . substr ( start + 1 , end - start - 1 ) ;
bool fileExcluded = false ;
2009-01-24 20:28:30 +01:00
+ + filelevel ;
2014-03-10 15:49:02 +01:00
if ( ! _settings ) {
fileExcluded = false ;
} else {
fileExcluded = _settings - > configurationExcluded ( includeFile ) ;
}
includeStack . push ( std : : pair < std : : string , bool > ( includeFile , fileExcluded ) ) ;
2009-01-24 20:28:30 +01:00
continue ;
}
2011-10-13 20:53:06 +02:00
else if ( line = = " #endfile " ) {
2010-02-06 09:30:48 +01:00
includeguard = false ;
2014-03-10 15:49:02 +01:00
includeStack . pop ( ) ;
2010-04-02 07:30:58 +02:00
if ( filelevel > 0 )
2009-01-24 20:28:30 +01:00
- - filelevel ;
continue ;
}
2011-10-13 20:53:06 +02:00
if ( line . compare ( 0 , 8 , " #define " ) = = 0 ) {
2012-08-25 13:24:17 +02:00
bool valid = false ;
2012-05-04 17:53:47 +02:00
for ( std : : string : : size_type pos = 8 ; pos < line . size ( ) ; + + pos ) {
2011-02-22 19:55:02 +01:00
char ch = line [ pos ] ;
2012-08-25 13:24:17 +02:00
if ( ch = = ' _ ' | | ( ch > = ' a ' & & ch < = ' z ' ) | | ( ch > = ' A ' & & ch < = ' Z ' ) | | ( pos > 8 & & ch > = ' 0 ' & & ch < = ' 9 ' ) ) {
valid = true ;
2011-02-22 19:55:02 +01:00
continue ;
2012-08-25 13:24:17 +02:00
}
if ( ch = = ' ' | | ch = = ' ( ' ) {
if ( valid )
break ;
}
2011-02-22 19:55:02 +01:00
valid = false ;
break ;
}
if ( ! valid )
line . clear ( ) ;
2011-10-13 20:53:06 +02:00
else {
2013-09-04 12:19:31 +02:00
std : : string definestr = line . substr ( 8 ) ;
const std : : string : : size_type spacepos = definestr . find ( " " ) ;
if ( spacepos ! = std : : string : : npos )
definestr [ spacepos ] = ' = ' ;
defines . insert ( definestr ) ;
const std : : string : : size_type separatorpos = definestr . find_first_of ( " =( " ) ;
if ( separatorpos ! = std : : string : : npos & & definestr [ separatorpos ] = = ' = ' ) {
const std : : string varname ( definestr . substr ( 0 , separatorpos ) ) ;
const std : : string value ( definestr . substr ( separatorpos + 1 ) ) ;
alldefinesmap [ varname ] = value ;
}
2009-07-25 13:58:34 +02:00
}
2009-07-22 18:47:50 +02:00
}
2010-04-02 07:30:58 +02:00
if ( ! line . empty ( ) & & line . compare ( 0 , 3 , " #if " ) ! = 0 )
2010-02-06 09:30:48 +01:00
includeguard = false ;
2012-12-27 16:52:31 +01:00
if ( line . compare ( 0 , 5 , " #line " ) = = 0 )
continue ;
2011-04-18 06:56:39 +02:00
if ( line . empty ( ) | | line [ 0 ] ! = ' # ' )
2011-01-31 03:37:37 +01:00
continue ;
2011-01-31 01:47:49 +01:00
2010-04-02 07:30:58 +02:00
if ( includeguard )
2009-01-24 20:28:30 +01:00
continue ;
2010-03-19 16:13:45 +01:00
bool from_negation = false ;
std : : string def = getdef ( line , true ) ;
2011-10-13 20:53:06 +02:00
if ( def . empty ( ) ) {
2010-03-19 16:13:45 +01:00
def = getdef ( line , false ) ;
// sub conditionals of ndef blocks need to be
// constructed _without_ the negated define
2010-04-02 07:30:58 +02:00
if ( ! def . empty ( ) )
2010-03-19 16:13:45 +01:00
from_negation = true ;
}
2011-10-13 20:53:06 +02:00
if ( ! def . empty ( ) ) {
2009-11-20 19:18:57 +01:00
int par = 0 ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type pos = 0 ; pos < def . length ( ) ; + + pos ) {
2010-04-02 07:30:58 +02:00
if ( def [ pos ] = = ' ( ' )
2009-11-20 19:18:57 +01:00
+ + par ;
2011-10-13 20:53:06 +02:00
else if ( def [ pos ] = = ' ) ' ) {
2009-11-20 19:18:57 +01:00
- - par ;
2010-04-02 07:30:58 +02:00
if ( par < 0 )
2009-11-20 19:18:57 +01:00
break ;
}
}
2011-10-13 20:53:06 +02:00
if ( par ! = 0 ) {
2010-04-09 21:40:37 +02:00
std : : ostringstream lineStream ;
lineStream < < __LINE__ ;
2009-11-20 19:18:57 +01:00
ErrorLogger : : ErrorMessage errmsg ;
ErrorLogger : : ErrorMessage : : FileLocation loc ;
2010-07-17 00:27:40 +02:00
loc . setfile ( filename ) ;
2009-11-20 19:18:57 +01:00
loc . line = linenr ;
errmsg . _callStack . push_back ( loc ) ;
2010-07-14 18:36:34 +02:00
errmsg . _severity = Severity : : fromString ( " error " ) ;
2010-11-11 19:54:43 +01:00
errmsg . setmsg ( " mismatching number of '(' and ')' in this line: " + def ) ;
2010-04-09 21:40:37 +02:00
errmsg . _id = " preprocessor " + lineStream . str ( ) ;
2009-11-20 19:18:57 +01:00
_errorLogger - > reportErr ( errmsg ) ;
ret . clear ( ) ;
return ret ;
}
2009-11-21 19:53:07 +01:00
// Replace defined constants
2013-09-04 12:19:31 +02:00
simplifyCondition ( alldefinesmap , def , false ) ;
2009-11-21 19:53:07 +01:00
2011-01-31 01:40:59 +01:00
if ( ! deflist . empty ( ) & & line . compare ( 0 , 6 , " #elif " ) = = 0 )
2009-01-24 20:28:30 +01:00
deflist . pop_back ( ) ;
2012-02-29 19:08:01 +01:00
// translate A==1 condition to A=1 configuration
if ( def . find ( " == " ) ! = std : : string : : npos ) {
// Check if condition match pattern "%var% == %num%"
// %var%
std : : string : : size_type pos = 0 ;
2014-03-18 21:41:47 +01:00
if ( std : : isalpha ( ( unsigned char ) def [ pos ] ) | | def [ pos ] = = ' _ ' ) {
2012-02-29 19:08:01 +01:00
+ + pos ;
2014-03-18 21:41:47 +01:00
while ( std : : isalnum ( ( unsigned char ) def [ pos ] ) | | def [ pos ] = = ' _ ' )
2012-02-29 19:08:01 +01:00
+ + pos ;
}
// ==
2014-03-09 14:26:16 +01:00
if ( def . compare ( pos , 2 , " == " ) = = 0 )
2012-02-29 19:08:01 +01:00
pos + = 2 ;
// %num%
if ( pos < def . size ( ) & & std : : isdigit ( def [ pos ] ) ) {
2014-03-09 14:26:16 +01:00
if ( def . compare ( pos , 2 , " 0x " ) = = 0 ) {
2012-02-29 19:08:01 +01:00
pos + = 2 ;
if ( pos > = def . size ( ) )
pos = 0 ;
2014-03-18 21:41:47 +01:00
while ( pos < def . size ( ) & & std : : isxdigit ( ( unsigned char ) def [ pos ] ) )
2012-02-29 19:08:01 +01:00
+ + pos ;
} else {
2014-03-18 21:41:47 +01:00
while ( pos < def . size ( ) & & std : : isdigit ( ( unsigned char ) def [ pos ] ) )
2012-02-29 19:08:01 +01:00
+ + pos ;
}
// Does the condition match the pattern "%var% == %num%"?
if ( pos = = def . size ( ) ) {
def . erase ( def . find ( " == " ) , 1 ) ;
}
}
}
2009-01-24 20:28:30 +01:00
deflist . push_back ( def ) ;
def = " " ;
2010-04-15 18:37:51 +02:00
2011-10-13 20:53:06 +02:00
for ( std : : list < std : : string > : : const_iterator it = deflist . begin ( ) ; it ! = deflist . end ( ) ; + + it ) {
2010-04-02 07:30:58 +02:00
if ( * it = = " 0 " )
2009-01-24 20:28:30 +01:00
break ;
2010-04-02 07:30:58 +02:00
if ( * it = = " 1 " | | * it = = " ! " )
2009-01-24 20:28:30 +01:00
continue ;
2009-09-05 19:01:57 +02:00
// don't add "T;T":
// treat two and more similar nested conditions as one
2011-10-13 20:53:06 +02:00
if ( def ! = * it ) {
2010-04-02 07:30:58 +02:00
if ( ! def . empty ( ) )
2009-09-05 19:01:57 +02:00
def + = " ; " ;
def + = * it ;
}
2011-02-07 20:26:29 +01:00
/* TODO: Fix TestPreprocessor::test7e (#2552)
2011-01-30 19:47:17 +01:00
else
{
std : : ostringstream lineStream ;
lineStream < < __LINE__ ;
ErrorLogger : : ErrorMessage errmsg ;
ErrorLogger : : ErrorMessage : : FileLocation loc ;
loc . setfile ( filename ) ;
loc . line = linenr ;
errmsg . _callStack . push_back ( loc ) ;
2011-02-07 20:26:29 +01:00
errmsg . _severity = Severity : : error ;
2011-01-30 19:47:17 +01:00
errmsg . setmsg ( * it + " is already guaranteed to be defined " ) ;
errmsg . _id = " preprocessor " + lineStream . str ( ) ;
_errorLogger - > reportErr ( errmsg ) ;
}
2011-02-07 20:26:29 +01:00
*/
2009-01-24 20:28:30 +01:00
}
2011-10-13 20:53:06 +02:00
if ( from_negation ) {
2010-03-19 16:13:45 +01:00
ndeflist . push_back ( deflist . back ( ) ) ;
2011-11-26 21:02:04 +01:00
deflist . back ( ) = " ! " ;
2010-03-19 16:13:45 +01:00
}
2011-10-13 20:53:06 +02:00
if ( std : : find ( ret . begin ( ) , ret . end ( ) , def ) = = ret . end ( ) ) {
2014-03-10 15:49:02 +01:00
if ( ! includeStack . top ( ) . second ) {
ret . push_back ( def ) ;
} else {
if ( _errorLogger & & _settings & & _settings - > debugwarnings ) {
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
const ErrorLogger : : ErrorMessage errmsg ( locationList , Severity : : debug ,
" Configuration not considered: " + def + " for file: " + includeStack . top ( ) . first , " debug " , false ) ;
_errorLogger - > reportErr ( errmsg ) ;
}
}
2010-03-19 16:13:45 +01:00
}
2009-01-24 20:28:30 +01:00
}
2011-10-13 20:53:06 +02:00
else if ( line . compare ( 0 , 5 , " #else " ) = = 0 & & ! deflist . empty ( ) ) {
if ( deflist . back ( ) = = " ! " ) {
2011-11-26 21:02:04 +01:00
deflist . back ( ) = ndeflist . back ( ) ;
2010-03-19 16:13:45 +01:00
ndeflist . pop_back ( ) ;
2011-10-13 20:53:06 +02:00
} else {
2010-04-09 21:40:37 +02:00
std : : string tempDef ( ( deflist . back ( ) = = " 1 " ) ? " 0 " : " 1 " ) ;
2011-11-26 21:02:04 +01:00
deflist . back ( ) = tempDef ;
2010-03-19 16:13:45 +01:00
}
2009-01-24 20:28:30 +01:00
}
2011-10-13 20:53:06 +02:00
else if ( line . compare ( 0 , 6 , " #endif " ) = = 0 & & ! deflist . empty ( ) ) {
2010-04-02 07:30:58 +02:00
if ( deflist . back ( ) = = " ! " )
2010-03-19 16:13:45 +01:00
ndeflist . pop_back ( ) ;
2009-01-24 20:28:30 +01:00
deflist . pop_back ( ) ;
2010-03-19 16:13:45 +01:00
}
2009-01-24 20:28:30 +01:00
}
2009-07-22 18:47:50 +02:00
// Remove defined constants from ifdef configurations..
2012-07-08 23:39:46 +02:00
std : : size_t count = 0 ;
2011-10-13 20:53:06 +02:00
for ( std : : list < std : : string > : : iterator it = ret . begin ( ) ; it ! = ret . end ( ) ; + + it ) {
2010-07-25 18:19:37 +02:00
if ( _errorLogger )
2010-08-03 16:36:21 +02:00
_errorLogger - > reportProgress ( filename , " Preprocessing (get configurations 2) " , ( 100 * count + + ) / ret . size ( ) ) ;
2010-07-25 18:19:37 +02:00
2009-07-25 13:58:34 +02:00
std : : string cfg ( * it ) ;
2011-10-13 20:53:06 +02:00
for ( std : : set < std : : string > : : const_iterator it2 = defines . begin ( ) ; it2 ! = defines . end ( ) ; + + it2 ) {
2009-07-22 18:47:50 +02:00
std : : string : : size_type pos = 0 ;
2009-07-25 13:58:34 +02:00
// Get name of define
std : : string defineName ( * it2 ) ;
2012-05-04 17:53:47 +02:00
if ( defineName . find_first_of ( " =( " ) ! = std : : string : : npos )
defineName . erase ( defineName . find_first_of ( " =( " ) ) ;
2009-07-25 13:58:34 +02:00
// Remove ifdef configurations that match the defineName
2011-10-13 20:53:06 +02:00
while ( ( pos = cfg . find ( defineName , pos ) ) ! = std : : string : : npos ) {
2012-05-04 17:53:47 +02:00
const std : : string : : size_type pos1 = pos ;
2009-07-22 18:47:50 +02:00
+ + pos ;
2010-04-02 07:30:58 +02:00
if ( pos1 > 0 & & cfg [ pos1 - 1 ] ! = ' ; ' )
2009-07-22 18:47:50 +02:00
continue ;
2012-05-04 17:53:47 +02:00
const std : : string : : size_type pos2 = pos1 + defineName . length ( ) ;
2010-04-02 07:30:58 +02:00
if ( pos2 < cfg . length ( ) & & cfg [ pos2 ] ! = ' ; ' )
2009-07-22 18:47:50 +02:00
continue ;
- - pos ;
2009-07-25 13:58:34 +02:00
cfg . erase ( pos , defineName . length ( ) ) ;
2009-07-22 18:47:50 +02:00
}
}
2011-10-13 20:53:06 +02:00
if ( cfg . length ( ) ! = it - > length ( ) ) {
2010-04-02 07:30:58 +02:00
while ( cfg . length ( ) > 0 & & cfg [ 0 ] = = ' ; ' )
2009-07-25 13:58:34 +02:00
cfg . erase ( 0 , 1 ) ;
2009-07-22 18:47:50 +02:00
2010-04-02 07:30:58 +02:00
while ( cfg . length ( ) > 0 & & cfg [ cfg . length ( ) - 1 ] = = ' ; ' )
2009-07-25 13:58:34 +02:00
cfg . erase ( cfg . length ( ) - 1 ) ;
2009-07-22 18:47:50 +02:00
std : : string : : size_type pos = 0 ;
2010-04-02 07:30:58 +02:00
while ( ( pos = cfg . find ( " ;; " , pos ) ) ! = std : : string : : npos )
2009-07-25 13:58:34 +02:00
cfg . erase ( pos , 1 ) ;
2009-07-22 18:47:50 +02:00
2009-07-25 13:58:34 +02:00
* it = cfg ;
2009-07-22 18:47:50 +02:00
}
}
2009-06-26 13:19:55 +02:00
// convert configurations: "defined(A) && defined(B)" => "A;B"
2011-10-13 20:53:06 +02:00
for ( std : : list < std : : string > : : iterator it = ret . begin ( ) ; it ! = ret . end ( ) ; + + it ) {
2009-06-26 13:19:55 +02:00
std : : string s ( * it ) ;
2011-10-13 20:53:06 +02:00
if ( s . find ( " && " ) ! = std : : string : : npos ) {
2009-11-20 19:18:57 +01:00
Tokenizer tokenizer ( _settings , _errorLogger ) ;
2013-01-27 17:58:54 +01:00
if ( ! tokenizer . tokenizeCondition ( s ) ) {
2010-04-09 21:40:37 +02:00
std : : ostringstream lineStream ;
lineStream < < __LINE__ ;
2009-11-20 19:18:57 +01:00
ErrorLogger : : ErrorMessage errmsg ;
ErrorLogger : : ErrorMessage : : FileLocation loc ;
2010-07-17 00:27:40 +02:00
loc . setfile ( filename ) ;
2009-11-20 19:18:57 +01:00
loc . line = 1 ;
errmsg . _callStack . push_back ( loc ) ;
2010-11-11 19:54:43 +01:00
errmsg . _severity = Severity : : error ;
errmsg . setmsg ( " Error parsing this: " + s ) ;
2010-04-09 21:40:37 +02:00
errmsg . _id = " preprocessor " + lineStream . str ( ) ;
2009-11-20 19:18:57 +01:00
_errorLogger - > reportErr ( errmsg ) ;
2009-11-09 00:11:52 +01:00
}
2009-06-26 13:19:55 +02:00
2009-08-29 23:00:54 +02:00
2009-06-26 13:19:55 +02:00
const Token * tok = tokenizer . tokens ( ) ;
2012-01-02 15:52:19 +01:00
std : : set < std : : string > varList ;
2011-10-13 20:53:06 +02:00
while ( tok ) {
if ( Token : : Match ( tok , " defined ( %var% ) " ) ) {
2012-01-02 15:52:19 +01:00
varList . insert ( tok - > strAt ( 2 ) ) ;
2009-06-26 13:19:55 +02:00
tok = tok - > tokAt ( 4 ) ;
2011-10-13 20:53:06 +02:00
if ( tok & & tok - > str ( ) = = " && " ) {
2009-06-26 13:19:55 +02:00
tok = tok - > next ( ) ;
}
2011-10-13 20:53:06 +02:00
} else if ( Token : : Match ( tok , " %var% ; " ) ) {
2012-01-02 15:52:19 +01:00
varList . insert ( tok - > str ( ) ) ;
2009-06-26 13:19:55 +02:00
tok = tok - > tokAt ( 2 ) ;
2011-10-13 20:53:06 +02:00
} else {
2009-06-26 13:19:55 +02:00
break ;
}
}
2010-04-15 18:37:51 +02:00
s = join ( varList , ' ; ' ) ;
2009-08-29 23:00:54 +02:00
2010-04-02 07:30:58 +02:00
if ( ! s . empty ( ) )
2009-06-26 13:19:55 +02:00
* it = s ;
}
}
2010-04-15 18:37:51 +02:00
// Convert configurations into a canonical form: B;C;A or C;A;B => A;B;C
2012-01-02 15:52:19 +01:00
for ( std : : list < std : : string > : : iterator it = ret . begin ( ) ; it ! = ret . end ( ) ; + + it )
* it = unify ( * it , ' ; ' ) ;
2010-04-15 18:37:51 +02:00
2009-08-29 23:00:54 +02:00
// Remove duplicates from the ret list..
2009-08-30 10:47:24 +02:00
ret . sort ( ) ;
ret . unique ( ) ;
2009-08-29 23:00:54 +02:00
2009-06-26 13:19:55 +02:00
// cleanup unhandled configurations..
2011-10-13 20:53:06 +02:00
for ( std : : list < std : : string > : : iterator it = ret . begin ( ) ; it ! = ret . end ( ) ; ) {
2009-12-13 15:23:44 +01:00
const std : : string s ( * it + " ; " ) ;
bool unhandled = false ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type pos = 0 ; pos < s . length ( ) ; + + pos ) {
2010-08-06 19:38:21 +02:00
const unsigned char c = static_cast < unsigned char > ( s [ pos ] ) ;
2009-12-13 15:23:44 +01:00
// ok with ";"
2010-04-02 07:30:58 +02:00
if ( c = = ' ; ' )
2009-12-13 15:23:44 +01:00
continue ;
// identifier..
2011-10-13 20:53:06 +02:00
if ( std : : isalpha ( c ) | | c = = ' _ ' ) {
2014-03-18 21:41:47 +01:00
while ( std : : isalnum ( ( unsigned char ) s [ pos ] ) | | s [ pos ] = = ' _ ' )
2009-12-13 15:23:44 +01:00
+ + pos ;
2011-10-13 20:53:06 +02:00
if ( s [ pos ] = = ' = ' ) {
2009-12-13 15:23:44 +01:00
+ + pos ;
2014-03-18 21:41:47 +01:00
while ( std : : isdigit ( ( unsigned char ) s [ pos ] ) )
2009-12-13 15:23:44 +01:00
+ + pos ;
2011-10-13 20:53:06 +02:00
if ( s [ pos ] ! = ' ; ' ) {
2009-12-13 15:23:44 +01:00
unhandled = true ;
break ;
}
}
- - pos ;
continue ;
}
// not ok..
2011-10-13 20:53:06 +02:00
else {
2009-12-13 15:23:44 +01:00
unhandled = true ;
break ;
}
}
2011-10-13 20:53:06 +02:00
if ( unhandled ) {
2009-06-26 13:19:55 +02:00
// unhandled ifdef configuration..
2011-10-13 20:53:06 +02:00
if ( _errorLogger & & _settings & & _settings - > debugwarnings ) {
2010-08-27 20:28:00 +02:00
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
2011-04-14 18:02:01 +02:00
const ErrorLogger : : ErrorMessage errmsg ( locationList , Severity : : debug , " unhandled configuration: " + * it , " debug " , false ) ;
2010-08-27 20:28:00 +02:00
_errorLogger - > reportErr ( errmsg ) ;
}
2009-06-26 13:19:55 +02:00
ret . erase ( it + + ) ;
2011-10-13 20:53:06 +02:00
} else {
2009-06-26 13:19:55 +02:00
+ + it ;
2009-12-13 15:23:44 +01:00
}
2009-06-26 13:19:55 +02:00
}
2009-01-24 20:28:30 +01:00
return ret ;
}
2011-07-16 09:24:27 +02:00
void Preprocessor : : simplifyCondition ( const std : : map < std : : string , std : : string > & cfg , std : : string & condition , bool match )
2009-11-21 19:53:07 +01:00
{
2012-01-06 08:42:07 +01:00
const Settings settings ;
Tokenizer tokenizer ( & settings , _errorLogger ) ;
2013-01-27 17:58:54 +01:00
if ( ! tokenizer . tokenizeCondition ( " ( " + condition + " ) " ) ) {
2011-08-02 21:06:27 +02:00
// If tokenize returns false, then there is syntax error in the
// code which we can't handle. So stop here.
return ;
}
2009-11-21 19:53:07 +01:00
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tokenizer . tokens ( ) , " ( %var% ) " ) ) {
2011-07-16 09:24:27 +02:00
std : : map < std : : string , std : : string > : : const_iterator var = cfg . find ( tokenizer . tokens ( ) - > strAt ( 1 ) ) ;
2011-10-13 20:53:06 +02:00
if ( var ! = cfg . end ( ) ) {
2011-02-23 22:08:24 +01:00
const std : : string & value = ( * var ) . second ;
condition = ( value = = " 0 " ) ? " 0 " : " 1 " ;
2011-10-13 20:53:06 +02:00
} else if ( match )
2010-03-04 18:51:25 +01:00
condition = " 0 " ;
return ;
}
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tokenizer . tokens ( ) , " ( ! %var% ) " ) ) {
2011-11-17 19:19:43 +01:00
std : : map < std : : string , std : : string > : : const_iterator var = cfg . find ( tokenizer . tokens ( ) - > strAt ( 2 ) ) ;
if ( var = = cfg . end ( ) )
condition = " 1 " ;
else if ( var - > second = = " 0 " )
2010-03-04 18:51:25 +01:00
condition = " 1 " ;
2010-04-02 07:30:58 +02:00
else if ( match )
2010-03-04 18:51:25 +01:00
condition = " 0 " ;
return ;
}
2009-11-21 19:53:07 +01:00
// replace variable names with values..
2011-10-13 20:53:06 +02:00
for ( Token * tok = const_cast < Token * > ( tokenizer . tokens ( ) ) ; tok ; tok = tok - > next ( ) ) {
2010-04-02 07:30:58 +02:00
if ( ! tok - > isName ( ) )
2009-12-11 19:28:37 +01:00
continue ;
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " defined ( %var% ) " ) ) {
2011-07-16 09:24:27 +02:00
if ( cfg . find ( tok - > strAt ( 2 ) ) ! = cfg . end ( ) )
2009-12-13 17:18:27 +01:00
tok - > str ( " 1 " ) ;
2010-04-02 07:30:58 +02:00
else if ( match )
2009-12-11 19:28:37 +01:00
tok - > str ( " 0 " ) ;
else
2009-12-13 17:18:27 +01:00
continue ;
2011-12-08 01:41:53 +01:00
tok - > deleteNext ( 3 ) ;
2009-12-11 19:28:37 +01:00
continue ;
}
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tok , " defined %var% " ) ) {
2011-07-16 09:24:27 +02:00
if ( cfg . find ( tok - > strAt ( 1 ) ) ! = cfg . end ( ) )
2009-12-13 17:18:27 +01:00
tok - > str ( " 1 " ) ;
2010-04-02 07:30:58 +02:00
else if ( match )
2009-12-11 19:28:37 +01:00
tok - > str ( " 0 " ) ;
else
2009-12-13 17:18:27 +01:00
continue ;
2009-12-11 19:28:37 +01:00
tok - > deleteNext ( ) ;
continue ;
}
2011-07-16 09:24:27 +02:00
const std : : map < std : : string , std : : string > : : const_iterator it = cfg . find ( tok - > str ( ) ) ;
2011-10-13 20:53:06 +02:00
if ( it ! = cfg . end ( ) ) {
if ( ! it - > second . empty ( ) ) {
2011-02-20 20:57:28 +01:00
// Tokenize the value
2014-01-12 13:15:54 +01:00
Tokenizer tokenizer2 ( & settings , _errorLogger ) ;
2013-01-27 17:58:54 +01:00
tokenizer2 . tokenizeCondition ( it - > second ) ;
2011-02-20 21:00:03 +01:00
2011-02-20 20:57:28 +01:00
// Copy the value tokens
std : : stack < Token * > link ;
2011-10-13 20:53:06 +02:00
for ( const Token * tok2 = tokenizer2 . tokens ( ) ; tok2 ; tok2 = tok2 - > next ( ) ) {
2011-02-20 20:57:28 +01:00
tok - > str ( tok2 - > str ( ) ) ;
if ( Token : : Match ( tok2 , " [{([] " ) )
link . push ( tok ) ;
2011-10-13 20:53:06 +02:00
else if ( ! link . empty ( ) & & Token : : Match ( tok2 , " [})]] " ) ) {
2011-02-20 20:57:28 +01:00
Token : : createMutualLinks ( link . top ( ) , tok ) ;
link . pop ( ) ;
}
2011-10-13 20:53:06 +02:00
if ( tok2 - > next ( ) ) {
2011-02-20 20:57:28 +01:00
tok - > insertToken ( " " ) ;
tok = tok - > next ( ) ;
}
}
2011-10-13 20:53:06 +02:00
} else if ( ( ! tok - > previous ( ) | | Token : : Match ( tok - > previous ( ) , " &&|%oror%|( " ) ) & &
( ! tok - > next ( ) | | Token : : Match ( tok - > next ( ) , " &&|%oror%|) " ) ) )
2010-08-26 21:33:45 +02:00
tok - > str ( " 1 " ) ;
else
tok - > deleteThis ( ) ;
2009-11-28 11:47:44 +01:00
}
2009-11-21 19:53:07 +01:00
}
// simplify calculations..
2012-12-01 02:02:45 +01:00
tokenizer . concatenateNegativeNumberAndAnyPositive ( ) ;
2009-12-11 22:32:44 +01:00
bool modified = true ;
2011-10-13 20:53:06 +02:00
while ( modified ) {
2009-12-11 22:32:44 +01:00
modified = false ;
2012-08-22 17:28:06 +02:00
modified | = tokenizer . simplifySizeof ( ) ;
2011-07-18 21:44:23 +02:00
modified | = tokenizer . simplifyCalculations ( ) ;
2013-06-19 21:29:39 +02:00
modified | = tokenizer . simplifyConstTernaryOp ( ) ;
2013-01-16 15:37:07 +01:00
modified | = tokenizer . simplifyRedundantParentheses ( ) ;
2011-10-13 20:53:06 +02:00
for ( Token * tok = const_cast < Token * > ( tokenizer . tokens ( ) ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " ! %num% " ) ) {
2009-12-11 22:32:44 +01:00
tok - > deleteThis ( ) ;
tok - > str ( tok - > str ( ) = = " 0 " ? " 1 " : " 0 " ) ;
modified = true ;
}
}
}
2009-11-21 19:53:07 +01:00
2011-10-13 20:53:06 +02:00
for ( Token * tok = const_cast < Token * > ( tokenizer . tokens ( ) ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " (|%oror%|&& %num% &&|%oror%|) " ) ) {
if ( tok - > next ( ) - > str ( ) ! = " 0 " ) {
2011-07-16 09:24:27 +02:00
tok - > next ( ) - > str ( " 1 " ) ;
}
}
}
2011-10-13 20:53:06 +02:00
for ( Token * tok = const_cast < Token * > ( tokenizer . tokens ( ) ) ; tok ; tok = tok - > next ( ) ) {
while ( Token : : Match ( tok , " (|%oror% %any% %oror% 1 " ) ) {
2011-12-08 01:41:53 +01:00
tok - > deleteNext ( 2 ) ;
2010-09-24 21:38:11 +02:00
if ( tok - > tokAt ( - 3 ) )
tok = tok - > tokAt ( - 3 ) ;
}
}
2010-04-02 07:30:58 +02:00
if ( Token : : simpleMatch ( tokenizer . tokens ( ) , " ( 1 ) " ) | |
Token : : simpleMatch ( tokenizer . tokens ( ) , " ( 1 || " ) )
2009-12-11 19:28:37 +01:00
condition = " 1 " ;
2010-04-02 07:30:58 +02:00
else if ( Token : : simpleMatch ( tokenizer . tokens ( ) , " ( 0 ) " ) )
2009-12-11 19:28:37 +01:00
condition = " 0 " ;
2009-11-21 19:53:07 +01:00
}
2012-03-15 20:04:34 +01:00
bool Preprocessor : : match_cfg_def ( std : : map < std : : string , std : : string > cfg , std : : string def )
2009-01-24 20:28:30 +01:00
{
2011-07-16 09:32:35 +02:00
/*
std : : cout < < " cfg: \" " ;
for ( std : : map < std : : string , std : : string > : : const_iterator it = cfg . begin ( ) ; it ! = cfg . end ( ) ; + + it )
{
std : : cout < < it - > first ;
if ( ! it - > second . empty ( ) )
std : : cout < < " = " < < it - > second ;
std : : cout < < " ; " ;
}
std : : cout < < " \" " ;
std : : cout < < " def: \" " < < def < < " \" \n " ;
*/
2009-06-26 13:19:55 +02:00
2012-03-15 20:04:34 +01:00
simplifyVarMap ( cfg ) ;
2009-12-13 17:18:27 +01:00
simplifyCondition ( cfg , def , true ) ;
2009-11-21 19:53:07 +01:00
2010-04-02 07:30:58 +02:00
if ( cfg . find ( def ) ! = cfg . end ( ) )
2009-07-30 10:10:34 +02:00
return true ;
2010-04-02 07:30:58 +02:00
if ( def = = " 0 " )
2009-01-24 20:28:30 +01:00
return false ;
2010-04-02 07:30:58 +02:00
if ( def = = " 1 " )
2009-01-24 20:28:30 +01:00
return true ;
return false ;
}
2013-08-12 18:12:49 +02:00
std : : string Preprocessor : : getcode ( const std : : string & filedata , const std : : string & cfg , const std : : string & filename )
2012-02-05 20:48:28 +01:00
{
// For the error report
unsigned int lineno = 0 ;
std : : ostringstream ret ;
bool match = true ;
std : : list < bool > matching_ifdef ;
std : : list < bool > matched_ifdef ;
// Create a map for the cfg for faster access to defines
2013-11-15 19:21:21 +01:00
std : : map < std : : string , std : : string > cfgmap ( getcfgmap ( cfg , _settings , filename ) ) ;
2012-02-05 20:48:28 +01:00
2011-08-03 07:28:37 +02:00
std : : stack < std : : string > filenames ;
filenames . push ( filename ) ;
std : : stack < unsigned int > lineNumbers ;
2009-01-24 20:28:30 +01:00
std : : istringstream istr ( filedata ) ;
std : : string line ;
2012-01-24 07:43:26 +01:00
while ( std : : getline ( istr , line ) ) {
2010-09-12 21:30:47 +02:00
+ + lineno ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2011-10-13 20:53:06 +02:00
if ( line . compare ( 0 , 11 , " #pragma asm " ) = = 0 ) {
2009-08-10 20:07:55 +02:00
ret < < " \n " ;
bool found_end = false ;
2011-10-13 20:53:06 +02:00
while ( getline ( istr , line ) ) {
if ( line . compare ( 0 , 14 , " #pragma endasm " ) = = 0 ) {
2009-08-10 20:07:55 +02:00
found_end = true ;
break ;
}
2010-02-12 18:15:15 +01:00
ret < < " \n " ;
2009-08-10 20:07:55 +02:00
}
2010-04-02 07:30:58 +02:00
if ( ! found_end )
2009-08-10 20:07:55 +02:00
break ;
2010-02-12 18:15:15 +01:00
2011-10-13 20:53:06 +02:00
if ( line . find ( " = " ) ! = std : : string : : npos ) {
2014-01-12 13:15:54 +01:00
Tokenizer tokenizer ( _settings , _errorLogger ) ;
2010-02-12 18:15:15 +01:00
line . erase ( 0 , sizeof ( " #pragma endasm " ) ) ;
2011-12-08 21:28:34 +01:00
std : : istringstream tempIstr ( line ) ;
2014-06-04 18:04:04 +02:00
tokenizer . tokenize ( tempIstr , " " , " " , true ) ;
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tokenizer . tokens ( ) , " ( %var% = %any% ) " ) ) {
2010-02-12 18:15:15 +01:00
ret < < " asm( " < < tokenizer . tokens ( ) - > strAt ( 1 ) < < " ); " ;
}
}
ret < < " \n " ;
2009-08-10 20:07:55 +02:00
continue ;
}
2011-08-14 18:35:34 +02:00
const std : : string def = getdef ( line , true ) ;
const std : : string ndef = getdef ( line , false ) ;
const bool emptymatch = matching_ifdef . empty ( ) | matched_ifdef . empty ( ) ;
2009-01-24 20:28:30 +01:00
2011-10-13 20:53:06 +02:00
if ( line . compare ( 0 , 8 , " #define " ) = = 0 ) {
2010-09-14 17:45:37 +02:00
match = true ;
2011-11-30 20:24:01 +01:00
2012-01-06 08:01:50 +01:00
if ( _settings ) {
2011-12-08 22:14:11 +01:00
typedef std : : set < std : : string > : : const_iterator It ;
2012-01-06 08:01:50 +01:00
for ( It it = _settings - > userUndefs . begin ( ) ; it ! = _settings - > userUndefs . end ( ) ; + + it ) {
2011-11-30 20:24:01 +01:00
std : : string : : size_type pos = line . find_first_not_of ( ' ' , 8 ) ;
if ( pos ! = std : : string : : npos ) {
std : : string : : size_type pos2 = line . find ( * it , pos ) ;
if ( ( pos2 ! = std : : string : : npos ) & &
( ( line . size ( ) = = pos2 + ( * it ) . size ( ) ) | |
( line [ pos2 + ( * it ) . size ( ) ] = = ' ' ) | |
( line [ pos2 + ( * it ) . size ( ) ] = = ' ( ' ) ) ) {
match = false ;
break ;
}
}
}
}
2010-09-14 17:45:37 +02:00
for ( std : : list < bool > : : const_iterator it = matching_ifdef . begin ( ) ; it ! = matching_ifdef . end ( ) ; + + it )
match & = bool ( * it ) ;
2011-10-13 20:53:06 +02:00
if ( match ) {
2010-09-14 17:45:37 +02:00
std : : string : : size_type pos = line . find_first_of ( " ( " , 8 ) ;
if ( pos = = std : : string : : npos )
cfgmap [ line . substr ( 8 ) ] = " " ;
2011-10-13 20:53:06 +02:00
else if ( line [ pos ] = = ' ' ) {
2011-07-16 16:59:06 +02:00
std : : string value ( line . substr ( pos + 1 ) ) ;
if ( cfgmap . find ( value ) ! = cfgmap . end ( ) )
value = cfgmap [ value ] ;
cfgmap [ line . substr ( 8 , pos - 8 ) ] = value ;
2011-10-13 20:53:06 +02:00
} else
2010-09-14 17:45:37 +02:00
cfgmap [ line . substr ( 8 , pos - 8 ) ] = " " ;
}
2009-07-22 18:47:50 +02:00
}
2012-01-24 07:43:26 +01:00
else if ( line . compare ( 0 , 7 , " #undef " ) = = 0 ) {
const std : : string name ( line . substr ( 7 ) ) ;
cfgmap . erase ( name ) ;
}
2011-10-13 20:53:06 +02:00
else if ( ! emptymatch & & line . compare ( 0 , 7 , " #elif ! " ) = = 0 ) {
if ( matched_ifdef . back ( ) ) {
2011-01-31 00:33:44 +01:00
matching_ifdef . back ( ) = false ;
2011-10-13 20:53:06 +02:00
} else {
if ( ! match_cfg_def ( cfgmap , ndef ) ) {
2011-01-31 00:33:44 +01:00
matching_ifdef . back ( ) = true ;
matched_ifdef . back ( ) = true ;
}
}
}
2011-10-13 20:53:06 +02:00
else if ( ! emptymatch & & line . compare ( 0 , 6 , " #elif " ) = = 0 ) {
if ( matched_ifdef . back ( ) ) {
2009-01-24 20:28:30 +01:00
matching_ifdef . back ( ) = false ;
2011-10-13 20:53:06 +02:00
} else {
if ( match_cfg_def ( cfgmap , def ) ) {
2009-01-24 20:28:30 +01:00
matching_ifdef . back ( ) = true ;
matched_ifdef . back ( ) = true ;
}
}
}
2013-06-19 21:29:39 +02:00
else if ( line . compare ( 0 , 4 , " #if " ) = = 0 ) {
matching_ifdef . push_back ( match_cfg_def ( cfgmap , line . substr ( 4 ) ) ) ;
matched_ifdef . push_back ( matching_ifdef . back ( ) ) ;
}
2011-10-13 20:53:06 +02:00
else if ( ! def . empty ( ) ) {
2013-06-19 21:29:39 +02:00
matching_ifdef . push_back ( cfgmap . find ( def ) ! = cfgmap . end ( ) ) ;
2009-01-24 20:28:30 +01:00
matched_ifdef . push_back ( matching_ifdef . back ( ) ) ;
}
2011-10-13 20:53:06 +02:00
else if ( ! ndef . empty ( ) ) {
2013-06-19 21:29:39 +02:00
matching_ifdef . push_back ( cfgmap . find ( ndef ) = = cfgmap . end ( ) ) ;
2009-01-24 20:28:30 +01:00
matched_ifdef . push_back ( matching_ifdef . back ( ) ) ;
}
2011-10-13 20:53:06 +02:00
else if ( ! emptymatch & & line = = " #else " ) {
2010-04-02 07:30:58 +02:00
if ( ! matched_ifdef . empty ( ) )
2009-01-24 20:28:30 +01:00
matching_ifdef . back ( ) = ! matched_ifdef . back ( ) ;
}
2011-10-13 20:53:06 +02:00
else if ( line . compare ( 0 , 6 , " #endif " ) = = 0 ) {
2010-04-02 07:30:58 +02:00
if ( ! matched_ifdef . empty ( ) )
2009-01-24 20:28:30 +01:00
matched_ifdef . pop_back ( ) ;
2010-04-02 07:30:58 +02:00
if ( ! matching_ifdef . empty ( ) )
2009-01-24 20:28:30 +01:00
matching_ifdef . pop_back ( ) ;
}
2011-10-13 20:53:06 +02:00
if ( ! line . empty ( ) & & line [ 0 ] = = ' # ' ) {
2009-01-24 20:28:30 +01:00
match = true ;
2010-04-02 07:30:58 +02:00
for ( std : : list < bool > : : const_iterator it = matching_ifdef . begin ( ) ; it ! = matching_ifdef . end ( ) ; + + it )
2009-01-24 20:28:30 +01:00
match & = bool ( * it ) ;
}
2009-11-13 22:12:29 +01:00
// #error => return ""
2011-10-13 20:53:06 +02:00
if ( match & & line . compare ( 0 , 6 , " #error " ) = = 0 ) {
2012-01-06 08:01:50 +01:00
if ( _settings & & ! _settings - > userDefines . empty ( ) ) {
Settings settings2 ( * _settings ) ;
Preprocessor preprocessor ( & settings2 , _errorLogger ) ;
2011-08-03 07:28:37 +02:00
preprocessor . error ( filenames . top ( ) , lineno , line ) ;
2010-09-12 21:30:47 +02:00
}
2009-11-13 22:12:29 +01:00
return " " ;
2010-09-12 21:30:47 +02:00
}
2009-05-17 18:51:29 +02:00
2011-02-11 18:51:22 +01:00
if ( ! match & & ( line . compare ( 0 , 8 , " #define " ) = = 0 | |
2011-10-13 20:53:06 +02:00
line . compare ( 0 , 6 , " #undef " ) = = 0 ) ) {
2009-05-17 18:51:29 +02:00
// Remove define that is not part of this configuration
line = " " ;
2011-10-13 20:53:06 +02:00
} else if ( line . compare ( 0 , 7 , " #file \" " ) = = 0 | |
line . compare ( 0 , 8 , " #endfile " ) = = 0 | |
line . compare ( 0 , 8 , " #define " ) = = 0 | |
2012-12-27 16:52:31 +01:00
line . compare ( 0 , 6 , " #line " ) = = 0 | |
2011-10-13 20:53:06 +02:00
line . compare ( 0 , 6 , " #undef " ) = = 0 ) {
2009-03-08 08:45:53 +01:00
// We must not remove #file tags or line numbers
// are corrupted. File tags are removed by the tokenizer.
2011-08-03 07:28:37 +02:00
// Keep location info updated
2011-10-13 20:53:06 +02:00
if ( line . compare ( 0 , 7 , " #file \" " ) = = 0 ) {
2011-08-03 07:28:37 +02:00
filenames . push ( line . substr ( 7 , line . size ( ) - 8 ) ) ;
lineNumbers . push ( lineno ) ;
lineno = 0 ;
2011-10-13 20:53:06 +02:00
} else if ( line . compare ( 0 , 8 , " #endfile " ) = = 0 ) {
2011-08-03 07:28:37 +02:00
if ( filenames . size ( ) > 1U )
filenames . pop ( ) ;
2011-10-13 20:53:06 +02:00
if ( ! lineNumbers . empty ( ) ) {
2011-08-03 07:28:37 +02:00
lineno = lineNumbers . top ( ) ;
lineNumbers . pop ( ) ;
}
}
2011-10-13 20:53:06 +02:00
} else if ( ! match | | line . compare ( 0 , 1 , " # " ) = = 0 ) {
2009-03-15 13:23:12 +01:00
// Remove #if, #else, #pragma etc, leaving only
2009-06-14 06:21:20 +02:00
// #define, #undef, #file and #endfile. and also lines
2009-03-15 13:23:12 +01:00
// which are not part of this configuration.
2009-01-24 20:28:30 +01:00
line = " " ;
2009-03-15 13:23:12 +01:00
}
2009-01-24 20:28:30 +01:00
ret < < line < < " \n " ;
}
2013-08-12 18:12:49 +02:00
if ( ! validateCfg ( ret . str ( ) , cfg ) ) {
2012-07-10 20:29:04 +02:00
return " " ;
}
2012-02-05 20:48:28 +01:00
return expandMacros ( ret . str ( ) , filename , cfg , _errorLogger ) ;
2009-01-24 20:28:30 +01:00
}
2010-12-29 12:43:29 +01:00
void Preprocessor : : error ( const std : : string & filename , unsigned int linenr , const std : : string & msg )
{
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
2011-10-13 20:53:06 +02:00
if ( ! filename . empty ( ) ) {
2010-12-29 12:43:29 +01:00
ErrorLogger : : ErrorMessage : : FileLocation loc ;
loc . line = linenr ;
loc . setfile ( filename ) ;
locationList . push_back ( loc ) ;
}
_errorLogger - > reportErr ( ErrorLogger : : ErrorMessage ( locationList ,
Severity : : error ,
msg ,
2011-04-14 18:02:01 +02:00
" preprocessorErrorDirective " ,
false ) ) ;
2010-12-29 12:43:29 +01:00
}
2010-07-21 11:50:04 +02:00
Preprocessor : : HeaderTypes Preprocessor : : getHeaderFileName ( std : : string & str )
2009-01-24 20:28:30 +01:00
{
std : : string result ;
2009-05-22 22:59:07 +02:00
std : : string : : size_type i = str . find_first_of ( " < \" " ) ;
2011-10-13 20:53:06 +02:00
if ( i = = std : : string : : npos ) {
2009-05-22 23:18:48 +02:00
str = " " ;
2010-07-21 11:50:04 +02:00
return NoHeader ;
2009-05-22 23:18:48 +02:00
}
2009-01-24 20:28:30 +01:00
2010-08-06 19:38:21 +02:00
char c = str [ i ] ;
2010-04-02 07:30:58 +02:00
if ( c = = ' < ' )
2009-05-22 22:59:07 +02:00
c = ' > ' ;
2011-10-13 20:53:06 +02:00
for ( i = i + 1 ; i < str . length ( ) ; + + i ) {
2010-04-02 07:30:58 +02:00
if ( str [ i ] = = c )
2009-01-24 20:28:30 +01:00
break ;
result . append ( 1 , str [ i ] ) ;
}
2010-07-21 13:16:42 +02:00
// Linux can't open include paths with \ separator, so fix them
std : : replace ( result . begin ( ) , result . end ( ) , ' \\ ' , ' / ' ) ;
2009-05-22 23:18:48 +02:00
str = result ;
2013-08-31 16:13:52 +02:00
return ( c = = ' \" ' ) ? UserHeader : SystemHeader ;
2009-01-24 20:28:30 +01:00
}
2011-10-17 20:12:46 +02:00
/**
* Try to open header
* @ param filename header name ( in / out )
* @ param includePaths paths where to look for the file
2012-10-24 01:20:14 +02:00
* @ param filePath path to the header file
2011-10-17 20:12:46 +02:00
* @ param fin file input stream ( in / out )
* @ return if file is opened then true is returned
*/
static bool openHeader ( std : : string & filename , const std : : list < std : : string > & includePaths , const std : : string & filePath , std : : ifstream & fin )
{
2012-09-15 12:53:11 +02:00
fin . open ( ( filePath + filename ) . c_str ( ) ) ;
if ( fin . is_open ( ) ) {
filename = filePath + filename ;
return true ;
}
2011-10-17 20:12:46 +02:00
std : : list < std : : string > includePaths2 ( includePaths ) ;
includePaths2 . push_front ( " " ) ;
for ( std : : list < std : : string > : : const_iterator iter = includePaths2 . begin ( ) ; iter ! = includePaths2 . end ( ) ; + + iter ) {
const std : : string nativePath ( Path : : toNativeSeparators ( * iter ) ) ;
fin . open ( ( nativePath + filename ) . c_str ( ) ) ;
if ( fin . is_open ( ) ) {
filename = nativePath + filename ;
return true ;
}
fin . clear ( ) ;
}
return false ;
}
2013-10-27 10:33:37 +01:00
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 )
2011-10-17 20:12:46 +02:00
{
const std : : string path ( filePath . substr ( 0 , 1 + filePath . find_last_of ( " \\ / " ) ) ) ;
2011-10-23 19:17:29 +02:00
// current #if indent level.
2013-02-10 07:43:09 +01:00
std : : stack < bool > : : size_type indent = 0 ;
2011-10-23 19:17:29 +02:00
// how deep does the #if match? this can never be bigger than "indent".
2013-02-10 07:43:09 +01:00
std : : stack < bool > : : size_type indentmatch = 0 ;
2011-10-17 20:12:46 +02:00
2011-10-24 08:11:44 +02:00
// has there been a true #if condition at the current indentmatch level?
// then no more #elif or #else can be true before the #endif is seen.
2013-02-10 07:43:09 +01:00
std : : stack < bool > elseIsTrueStack ;
2011-10-24 08:11:44 +02:00
2011-10-25 18:16:03 +02:00
unsigned int linenr = 0 ;
2011-12-09 21:57:06 +01:00
std : : set < std : : string > undefs = _settings ? _settings - > userUndefs : std : : set < std : : string > ( ) ;
2013-01-26 19:11:58 +01:00
if ( _errorLogger )
_errorLogger - > reportProgress ( filePath , " Preprocessor (handleIncludes) " , 0 ) ;
2011-10-17 20:12:46 +02:00
std : : ostringstream ostr ;
std : : istringstream istr ( code ) ;
std : : string line ;
2011-12-09 21:57:06 +01:00
bool suppressCurrentCodePath = false ;
2011-10-17 20:12:46 +02:00
while ( std : : getline ( istr , line ) ) {
2011-10-25 18:16:03 +02:00
+ + linenr ;
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return " " ;
2012-09-15 09:34:41 +02:00
// has there been a true #if condition at the current indentmatch level?
// then no more #elif or #else can be true before the #endif is seen.
2013-02-10 07:43:09 +01:00
while ( elseIsTrueStack . size ( ) ! = indentmatch + 1 ) {
if ( elseIsTrueStack . size ( ) < indentmatch + 1 ) {
elseIsTrueStack . push ( true ) ;
} else {
elseIsTrueStack . pop ( ) ;
}
}
2013-10-18 06:47:55 +02:00
if ( elseIsTrueStack . empty ( ) ) {
2013-10-19 09:52:19 +02:00
writeError ( filePath , linenr , _errorLogger , " syntaxError " , " Syntax error in preprocessor code " ) ;
2013-10-18 06:47:55 +02:00
return " " ;
}
2013-02-10 07:43:09 +01:00
std : : stack < bool > : : reference elseIsTrue = elseIsTrueStack . top ( ) ;
2012-09-15 09:34:41 +02:00
2013-08-18 18:04:06 +02:00
if ( line = = " #pragma once " ) {
2013-10-27 10:33:37 +01:00
pragmaOnce . insert ( filePath ) ;
2013-08-18 18:04:06 +02:00
} else if ( line . compare ( 0 , 7 , " #ifdef " ) = = 0 ) {
2011-12-09 21:57:06 +01:00
if ( indent = = indentmatch ) {
2011-12-13 21:14:41 +01:00
const std : : string tag = getdef ( line , true ) ;
2011-12-09 21:57:06 +01:00
if ( defs . find ( tag ) ! = defs . end ( ) ) {
elseIsTrue = false ;
indentmatch + + ;
} else if ( undefs . find ( tag ) ! = undefs . end ( ) ) {
elseIsTrue = true ;
indentmatch + + ;
suppressCurrentCodePath = true ;
}
2011-10-24 08:11:44 +02:00
}
2011-10-17 20:12:46 +02:00
+ + indent ;
2011-10-24 08:11:44 +02:00
if ( indent = = indentmatch + 1 )
elseIsTrue = true ;
2011-10-17 20:12:46 +02:00
} else if ( line . compare ( 0 , 8 , " #ifndef " ) = = 0 ) {
2011-12-09 21:57:06 +01:00
if ( indent = = indentmatch ) {
2011-12-13 21:14:41 +01:00
const std : : string tag = getdef ( line , false ) ;
2011-12-09 21:57:06 +01:00
if ( defs . find ( tag ) = = defs . end ( ) ) {
elseIsTrue = false ;
indentmatch + + ;
} else if ( undefs . find ( tag ) ! = undefs . end ( ) ) {
elseIsTrue = false ;
indentmatch + + ;
suppressCurrentCodePath = false ;
}
}
2011-12-13 21:14:41 +01:00
+ + indent ;
if ( indent = = indentmatch + 1 )
elseIsTrue = true ;
2013-06-21 20:10:46 +02:00
} else if ( line . compare ( 0 , 4 , " #if " ) = = 0 ) {
if ( ! suppressCurrentCodePath & & indent = = indentmatch & & match_cfg_def ( defs , line . substr ( 4 ) ) ) {
2011-10-24 08:11:44 +02:00
elseIsTrue = false ;
2011-10-23 19:17:29 +02:00
indentmatch + + ;
2011-10-24 08:11:44 +02:00
}
2011-10-23 19:17:29 +02:00
+ + indent ;
2011-10-24 08:11:44 +02:00
if ( indent = = indentmatch + 1 )
2013-06-21 20:10:46 +02:00
elseIsTrue = true ; // this value doesn't matter when suppressCurrentCodePath is true
2011-10-24 08:11:44 +02:00
} else if ( line . compare ( 0 , 6 , " #elif " ) = = 0 | | line . compare ( 0 , 5 , " #else " ) = = 0 ) {
if ( ! elseIsTrue ) {
2011-12-09 21:57:06 +01:00
if ( indentmatch = = indent ) {
2011-10-24 08:11:44 +02:00
indentmatch = indent - 1 ;
2011-12-09 21:57:06 +01:00
}
2011-10-24 08:11:44 +02:00
} else {
2011-12-09 21:57:06 +01:00
if ( indentmatch = = indent ) {
2011-10-24 08:11:44 +02:00
indentmatch = indent - 1 ;
2011-12-09 21:57:06 +01:00
} else if ( indentmatch = = indent - 1 ) {
2011-10-24 08:11:44 +02:00
if ( line . compare ( 0 , 5 , " #else " ) = = 0 | | match_cfg_def ( defs , line . substr ( 6 ) ) ) {
indentmatch = indent ;
elseIsTrue = false ;
}
}
}
2011-12-17 15:23:55 +01:00
} else if ( line . compare ( 0 , 6 , " #endif " ) = = 0 ) {
2011-12-12 19:35:25 +01:00
if ( indent > 0 )
- - indent ;
if ( indentmatch > indent | | indent = = 0 ) {
2011-10-17 20:12:46 +02:00
indentmatch = indent ;
2011-10-24 08:11:44 +02:00
elseIsTrue = false ;
2011-12-09 21:57:06 +01:00
suppressCurrentCodePath = false ;
2011-10-24 08:11:44 +02:00
}
2011-10-17 20:12:46 +02:00
} else if ( indentmatch = = indent ) {
2012-01-21 12:51:54 +01:00
if ( ! suppressCurrentCodePath & & line . compare ( 0 , 8 , " #define " ) = = 0 ) {
const unsigned int endOfDefine = 8 ;
std : : string : : size_type endOfTag = line . find_first_of ( " ( " , endOfDefine ) ;
std : : string tag ;
// define a symbol
if ( endOfTag = = std : : string : : npos ) {
tag = line . substr ( endOfDefine ) ;
2011-12-09 21:57:06 +01:00
defs [ tag ] = " " ;
2012-01-21 12:51:54 +01:00
} else {
tag = line . substr ( endOfDefine , endOfTag - endOfDefine ) ;
2011-10-17 20:12:46 +02:00
2012-01-21 12:51:54 +01:00
// define a function-macro
if ( line [ endOfTag ] = = ' ( ' ) {
defs [ tag ] = " " ;
}
// define value
else {
+ + endOfTag ;
const std : : string & value = line . substr ( endOfTag , line . size ( ) - endOfTag ) ;
if ( defs . find ( value ) ! = defs . end ( ) )
defs [ tag ] = defs [ value ] ;
else
defs [ tag ] = value ;
}
2011-12-09 21:57:06 +01:00
}
if ( undefs . find ( tag ) ! = undefs . end ( ) ) {
defs . erase ( tag ) ;
2011-10-17 20:12:46 +02:00
}
}
2011-12-09 21:57:06 +01:00
else if ( ! suppressCurrentCodePath & & line . compare ( 0 , 7 , " #undef " ) = = 0 ) {
2011-10-24 19:51:00 +02:00
defs . erase ( line . substr ( 7 ) ) ;
}
2011-12-09 21:57:06 +01:00
else if ( ! suppressCurrentCodePath & & line . compare ( 0 , 7 , " #error " ) = = 0 ) {
2011-10-25 19:55:47 +02:00
error ( filePath , linenr , line . substr ( 7 ) ) ;
}
2011-12-09 21:57:06 +01:00
else if ( ! suppressCurrentCodePath & & line . compare ( 0 , 9 , " #include " ) = = 0 ) {
2011-11-03 19:05:48 +01:00
std : : string filename ( line . substr ( 9 ) ) ;
const HeaderTypes headerType = getHeaderFileName ( filename ) ;
if ( headerType = = NoHeader ) {
ostr < < std : : endl ;
continue ;
}
// try to open file
2011-12-08 22:17:50 +01:00
std : : string filepath ;
if ( headerType = = UserHeader )
filepath = path ;
2011-11-03 19:05:48 +01:00
std : : ifstream fin ;
2011-12-08 22:17:50 +01:00
if ( ! openHeader ( filename , includePaths , filepath , fin ) ) {
2013-03-13 06:48:33 +01:00
missingInclude ( Path : : toNativeSeparators ( filePath ) ,
linenr ,
filename ,
headerType
) ;
2011-11-03 19:05:48 +01:00
ostr < < std : : endl ;
continue ;
}
// Prevent that files are recursively included
if ( std : : find ( includes . begin ( ) , includes . end ( ) , filename ) ! = includes . end ( ) ) {
ostr < < std : : endl ;
continue ;
}
includes . push_back ( filename ) ;
2013-08-18 18:04:06 +02:00
// Don't include header if it's already included and contains #pragma once
2013-10-27 10:33:37 +01:00
if ( pragmaOnce . find ( filename ) ! = pragmaOnce . end ( ) ) {
2013-08-18 18:04:06 +02:00
ostr < < std : : endl ;
continue ;
}
2011-11-03 19:05:48 +01:00
ostr < < " #file \" " < < filename < < " \" \n "
2013-08-18 18:04:06 +02:00
< < handleIncludes ( read ( fin , filename ) , filename , includePaths , defs , pragmaOnce , includes ) < < std : : endl
2011-11-03 19:05:48 +01:00
< < " #endfile \n " ;
continue ;
}
2011-12-09 21:57:06 +01:00
if ( ! suppressCurrentCodePath )
ostr < < line ;
2011-10-17 20:12:46 +02:00
}
// A line has been read..
ostr < < " \n " ;
}
return ostr . str ( ) ;
}
2010-01-14 22:00:14 +01:00
2011-01-06 20:01:09 +01:00
void Preprocessor : : handleIncludes ( std : : string & code , const std : : string & filePath , const std : : list < std : : string > & includePaths )
2009-01-24 20:28:30 +01:00
{
2011-01-06 20:01:09 +01:00
std : : list < std : : string > paths ;
std : : string path ;
path = filePath ;
path . erase ( 1 + path . find_last_of ( " \\ / " ) ) ;
paths . push_back ( path ) ;
2009-01-24 20:28:30 +01:00
std : : string : : size_type pos = 0 ;
2011-01-06 20:01:09 +01:00
std : : string : : size_type endfilePos = 0 ;
std : : set < std : : string > handledFiles ;
2011-10-13 20:53:06 +02:00
while ( ( pos = code . find ( " #include " , pos ) ) ! = std : : string : : npos ) {
2013-07-24 13:06:59 +02:00
if ( _settings & & _settings - > terminated ( ) )
return ;
2009-01-24 20:28:30 +01:00
// Accept only includes that are at the start of a line
2011-10-13 20:53:06 +02:00
if ( pos > 0 & & code [ pos - 1 ] ! = ' \n ' ) {
2009-01-24 20:28:30 +01:00
pos + = 8 ; // length of "#include"
continue ;
}
2011-01-06 20:01:09 +01:00
// If endfile is encountered, we have moved to a next file in our stack,
// so remove last path in our list.
2014-04-23 20:50:16 +02:00
while ( ! paths . empty ( ) & & ( endfilePos = code . find ( " \n #endfile " , endfilePos ) ) ! = std : : string : : npos & & endfilePos < pos ) {
2011-01-06 20:01:09 +01:00
paths . pop_back ( ) ;
endfilePos + = 9 ; // size of #endfile
}
endfilePos = pos ;
2009-01-24 20:28:30 +01:00
std : : string : : size_type end = code . find ( " \n " , pos ) ;
std : : string filename = code . substr ( pos , end - pos ) ;
// Remove #include clause
code . erase ( pos , end - pos ) ;
2010-07-21 11:50:04 +02:00
HeaderTypes headerType = getHeaderFileName ( filename ) ;
if ( headerType = = NoHeader )
2009-01-24 20:28:30 +01:00
continue ;
// filename contains now a file name e.g. "menu.h"
std : : string processedFile ;
2011-12-08 22:17:50 +01:00
std : : string filepath ;
2012-12-28 10:31:56 +01:00
if ( headerType = = UserHeader & & ! paths . empty ( ) )
2011-12-08 22:17:50 +01:00
filepath = paths . back ( ) ;
2010-02-09 21:26:15 +01:00
std : : ifstream fin ;
2011-12-08 22:17:50 +01:00
const bool fileOpened ( openHeader ( filename , includePaths , filepath , fin ) ) ;
2009-01-24 20:28:30 +01:00
2011-10-13 20:53:06 +02:00
if ( fileOpened ) {
2014-04-02 13:56:34 +02:00
filename = Path : : simplifyPath ( filename ) ;
2010-09-15 21:03:21 +02:00
std : : string tempFile = filename ;
2010-02-09 21:26:15 +01:00
std : : transform ( tempFile . begin ( ) , tempFile . end ( ) , tempFile . begin ( ) , tolowerWrapper ) ;
2011-10-13 20:53:06 +02:00
if ( handledFiles . find ( tempFile ) ! = handledFiles . end ( ) ) {
2010-02-09 21:26:15 +01:00
// We have processed this file already once, skip
2010-12-15 18:45:53 +01:00
// it this time to avoid eternal loop.
2010-02-14 20:55:21 +01:00
fin . close ( ) ;
2010-02-09 21:26:15 +01:00
continue ;
}
2011-01-06 20:01:09 +01:00
handledFiles . insert ( tempFile ) ;
2012-01-05 18:37:15 +01:00
processedFile = Preprocessor : : read ( fin , filename ) ;
2010-02-09 21:26:15 +01:00
fin . close ( ) ;
}
2011-10-13 20:53:06 +02:00
if ( ! processedFile . empty ( ) ) {
2009-01-24 20:28:30 +01:00
// Remove space characters that are after or before new line character
2012-04-06 10:49:21 +02:00
processedFile = " #file \" " + Path : : fromNativeSeparators ( filename ) + " \" \n " + processedFile + " \n #endfile " ;
2009-01-24 20:28:30 +01:00
code . insert ( pos , processedFile ) ;
2011-01-06 20:01:09 +01:00
path = filename ;
path . erase ( 1 + path . find_last_of ( " \\ / " ) ) ;
paths . push_back ( path ) ;
2013-01-20 14:22:05 +01:00
} else if ( ! fileOpened & & _settings ) {
2012-05-16 11:59:45 +02:00
std : : string f = filePath ;
// Determine line number of include
unsigned int linenr = 1 ;
unsigned int level = 0 ;
for ( std : : string : : size_type p = 1 ; p < = pos ; + + p ) {
if ( level = = 0 & & code [ pos - p ] = = ' \n ' )
+ + linenr ;
else if ( code . compare ( pos - p , 9 , " #endfile \n " ) = = 0 ) {
+ + level ;
} else if ( code . compare ( pos - p , 6 , " #file " ) = = 0 ) {
if ( level = = 0 ) {
linenr - - ;
const std : : string : : size_type pos1 = pos - p + 7 ;
const std : : string : : size_type pos2 = code . find_first_of ( " \" \n " , pos1 ) ;
f = code . substr ( pos1 , ( pos2 = = std : : string : : npos ) ? pos2 : ( pos2 - pos1 ) ) ;
break ;
2010-09-03 13:30:49 +02:00
}
2012-05-16 11:59:45 +02:00
- - level ;
2010-09-03 13:30:49 +02:00
}
2012-05-16 11:59:45 +02:00
}
2010-09-03 13:30:49 +02:00
2013-03-13 06:48:33 +01:00
missingInclude ( Path : : toNativeSeparators ( f ) ,
linenr ,
filename ,
headerType ) ;
2009-07-25 21:10:30 +02:00
}
2009-01-24 20:28:30 +01:00
}
}
2010-12-29 12:43:29 +01:00
// Report that include is missing
2013-03-13 06:48:33 +01:00
void Preprocessor : : missingInclude ( const std : : string & filename , unsigned int linenr , const std : : string & header , HeaderTypes headerType )
2010-12-29 12:43:29 +01:00
{
2014-05-03 19:31:15 +02:00
const std : : string fname = Path : : fromNativeSeparators ( filename ) ;
if ( _settings - > nomsg . isSuppressed ( " missingInclude " , fname , linenr ) )
return ;
if ( headerType = = SystemHeader & & _settings - > nomsg . isSuppressed ( " missingIncludeSystem " , fname , linenr ) )
return ;
2013-03-13 06:48:33 +01:00
2014-05-03 19:31:15 +02:00
if ( headerType = = SystemHeader )
missingSystemIncludeFlag = true ;
else
missingIncludeFlag = true ;
if ( _errorLogger & & _settings - > checkConfiguration ) {
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
if ( ! filename . empty ( ) ) {
ErrorLogger : : ErrorMessage : : FileLocation loc ;
loc . line = linenr ;
loc . setfile ( Path : : toNativeSeparators ( filename ) ) ;
locationList . push_back ( loc ) ;
}
ErrorLogger : : ErrorMessage errmsg ( locationList , Severity : : information ,
( headerType = = SystemHeader ) ?
" Include file: < " + header + " > not found. Please note: Cppcheck does not need standard library headers to get proper results. " :
" Include file: \" " + header + " \" not found. " ,
( headerType = = SystemHeader ) ? " missingIncludeSystem " : " missingInclude " ,
false ) ;
errmsg . file0 = file0 ;
_errorLogger - > reportInfo ( errmsg ) ;
2010-12-29 12:43:29 +01:00
}
}
2010-06-19 09:09:47 +02:00
/**
* Skip string in line . A string begins and ends with either a & quot ; or a & apos ;
* @ param line the string
* @ param pos in = start position of string , out = end position of string
*/
static void skipstring ( const std : : string & line , std : : string : : size_type & pos )
{
2010-08-06 19:38:21 +02:00
const char ch = line [ pos ] ;
2010-06-19 09:09:47 +02:00
+ + pos ;
2011-10-13 20:53:06 +02:00
while ( pos < line . size ( ) & & line [ pos ] ! = ch ) {
2010-06-19 09:09:47 +02:00
if ( line [ pos ] = = ' \\ ' )
+ + pos ;
+ + pos ;
}
}
2013-08-10 23:28:02 +02:00
/**
* Remove heading and trailing whitespaces from the input parameter .
2013-08-11 17:04:13 +02:00
* @ param s The string to trim .
2013-08-10 23:28:02 +02:00
*/
static std : : string trim ( const std : : string & s )
{
std : : string : : size_type beg = s . find_first_not_of ( " \t " ) ;
if ( beg = = std : : string : : npos )
return s ;
std : : string : : size_type end = s . find_last_not_of ( " \t " ) ;
if ( end = = std : : string : : npos )
return s . substr ( beg ) ;
return s . substr ( beg , end - beg + 1 ) ;
}
2010-06-19 09:09:47 +02:00
/**
* @ brief get parameters from code . For example ' foo ( 1 , 2 ) ' = > ' 1 ' , ' 2 '
* @ param line in : The code
* @ param pos in : Position to the ' ( ' . out : Position to the ' ) '
* @ param params out : The extracted parameters
* @ param numberOfNewlines out : number of newlines in the macro call
2013-01-16 15:37:07 +01:00
* @ param endFound out : was the end parentheses found ?
2010-06-19 09:09:47 +02:00
*/
static void getparams ( const std : : string & line ,
2010-06-19 22:24:10 +02:00
std : : string : : size_type & pos ,
2010-06-19 09:09:47 +02:00
std : : vector < std : : string > & params ,
unsigned int & numberOfNewlines ,
bool & endFound )
{
params . clear ( ) ;
numberOfNewlines = 0 ;
endFound = false ;
2010-06-19 12:03:39 +02:00
if ( line [ pos ] = = ' ' )
pos + + ;
2010-06-19 09:09:47 +02:00
2010-06-19 12:03:39 +02:00
if ( line [ pos ] ! = ' ( ' )
2010-06-19 09:09:47 +02:00
return ;
2010-12-15 18:45:53 +01:00
// parentheses level
2010-06-19 09:09:47 +02:00
int parlevel = 0 ;
2010-06-19 12:03:39 +02:00
// current parameter data
2010-06-19 09:09:47 +02:00
std : : string par ;
// scan for parameters..
2011-10-13 20:53:06 +02:00
for ( ; pos < line . length ( ) ; + + pos ) {
2013-01-16 15:37:07 +01:00
// increase parentheses level
2011-10-13 20:53:06 +02:00
if ( line [ pos ] = = ' ( ' ) {
2010-06-19 09:09:47 +02:00
+ + parlevel ;
if ( parlevel = = 1 )
continue ;
}
2013-01-16 15:37:07 +01:00
// decrease parentheses level
2011-10-13 20:53:06 +02:00
else if ( line [ pos ] = = ' ) ' ) {
2010-06-19 09:09:47 +02:00
- - parlevel ;
2011-10-13 20:53:06 +02:00
if ( parlevel < = 0 ) {
2010-06-19 09:09:47 +02:00
endFound = true ;
2013-08-10 23:28:02 +02:00
params . push_back ( trim ( par ) ) ;
2010-06-19 09:09:47 +02:00
break ;
}
}
// string
2011-10-13 20:53:06 +02:00
else if ( line [ pos ] = = ' \" ' | | line [ pos ] = = ' \' ' ) {
2010-06-19 12:03:39 +02:00
const std : : string : : size_type p = pos ;
skipstring ( line , pos ) ;
if ( pos = = line . length ( ) )
2010-06-19 09:09:47 +02:00
break ;
2010-06-19 12:03:39 +02:00
par + = line . substr ( p , pos + 1 - p ) ;
2010-06-19 09:09:47 +02:00
continue ;
}
// count newlines. the expanded macro must have the same number of newlines
2011-10-13 20:53:06 +02:00
else if ( line [ pos ] = = ' \n ' ) {
2010-06-19 09:09:47 +02:00
+ + numberOfNewlines ;
continue ;
}
// new parameter
2011-10-13 20:53:06 +02:00
if ( parlevel = = 1 & & line [ pos ] = = ' , ' ) {
2013-08-10 23:28:02 +02:00
params . push_back ( trim ( par ) ) ;
2010-06-19 09:09:47 +02:00
par = " " ;
}
// spaces are only added if needed
2011-10-13 20:53:06 +02:00
else if ( line [ pos ] = = ' ' ) {
2010-06-19 09:09:47 +02:00
// Add space only if it is needed
2014-03-18 21:41:47 +01:00
if ( par . size ( ) & & std : : isalnum ( ( unsigned char ) par [ par . length ( ) - 1 ] ) ) {
2010-06-19 09:09:47 +02:00
par + = ' ' ;
}
}
// add character to current parameter
2013-10-13 17:42:06 +02:00
else if ( parlevel > = 1 & & line [ pos ] ! = Preprocessor : : macroChar ) {
2010-06-19 12:03:39 +02:00
par . append ( 1 , line [ pos ] ) ;
2010-06-19 09:09:47 +02:00
}
}
}
2009-07-08 17:14:34 +02:00
/** @brief Class that the preprocessor uses when it expands macros. This class represents a preprocessor macro */
2011-10-13 20:53:06 +02:00
class PreprocessorMacro {
2009-01-24 20:28:30 +01:00
private :
2010-12-01 18:00:55 +01:00
Settings settings ;
2010-01-13 21:50:44 +01:00
/** tokens of this macro */
2009-01-24 20:28:30 +01:00
Tokenizer tokenizer ;
2010-01-13 21:50:44 +01:00
/** macro parameters */
2009-01-24 20:28:30 +01:00
std : : vector < std : : string > _params ;
2010-01-13 21:50:44 +01:00
/** name of macro */
2009-01-24 20:28:30 +01:00
std : : string _name ;
2010-01-13 21:50:44 +01:00
/** macro definition in plain text */
const std : : string _macro ;
/** prefix that is used by cppcheck to separate macro parameters. Always "__cppcheck__" */
2009-09-19 23:09:05 +02:00
const std : : string _prefix ;
2009-01-24 20:28:30 +01:00
2012-05-14 20:46:23 +02:00
/** does this macro take a variable number of parameters? */
bool _variadic ;
2010-12-15 18:45:53 +01:00
/** The macro has parentheses but no parameters.. "AAA()" */
2009-06-19 16:42:47 +02:00
bool _nopar ;
2009-10-10 22:01:33 +02:00
/** disabled assignment operator */
void operator = ( const PreprocessorMacro & ) ;
2010-06-19 12:03:39 +02:00
/** @brief expand inner macro */
std : : vector < std : : string > expandInnerMacros ( const std : : vector < std : : string > & params1 ,
2011-10-13 20:53:06 +02:00
const std : : map < std : : string , PreprocessorMacro * > & macros ) const {
2010-06-19 12:03:39 +02:00
std : : string innerMacroName ;
// Is there an inner macro..
{
2011-10-27 10:54:50 +02:00
const Token * tok = Token : : findsimplematch ( tokens ( ) , " ) " ) ;
2010-06-19 12:03:39 +02:00
if ( ! Token : : Match ( tok , " ) %var% ( " ) )
return params1 ;
innerMacroName = tok - > strAt ( 1 ) ;
tok = tok - > tokAt ( 3 ) ;
unsigned int par = 0 ;
2011-10-13 20:53:06 +02:00
while ( Token : : Match ( tok , " %var% ,|) " ) ) {
2010-06-19 12:03:39 +02:00
tok = tok - > tokAt ( 2 ) ;
par + + ;
}
if ( tok | | par ! = params1 . size ( ) )
return params1 ;
}
std : : vector < std : : string > params2 ( params1 ) ;
2011-10-13 20:53:06 +02:00
for ( unsigned int ipar = 0 ; ipar < params1 . size ( ) ; + + ipar ) {
2010-06-19 12:03:39 +02:00
const std : : string s ( innerMacroName + " ( " ) ;
std : : string param ( params1 [ ipar ] ) ;
2011-10-13 20:53:06 +02:00
if ( param . compare ( 0 , s . length ( ) , s ) = = 0 & & param [ param . length ( ) - 1 ] = = ' ) ' ) {
2010-06-19 12:03:39 +02:00
std : : vector < std : : string > innerparams ;
2010-06-19 22:24:10 +02:00
std : : string : : size_type pos = s . length ( ) - 1 ;
2010-06-19 12:03:39 +02:00
unsigned int num = 0 ;
bool endFound = false ;
getparams ( param , pos , innerparams , num , endFound ) ;
2011-10-13 20:53:06 +02:00
if ( pos = = param . length ( ) - 1 & & num = = 0 & & endFound & & innerparams . size ( ) = = params1 . size ( ) ) {
2010-06-19 12:03:39 +02:00
// Is inner macro defined?
std : : map < std : : string , PreprocessorMacro * > : : const_iterator it = macros . find ( innerMacroName ) ;
2011-10-13 20:53:06 +02:00
if ( it ! = macros . end ( ) ) {
2010-06-19 12:03:39 +02:00
// expand the inner macro
const PreprocessorMacro * innerMacro = it - > second ;
std : : string innercode ;
std : : map < std : : string , PreprocessorMacro * > innermacros = macros ;
innermacros . erase ( innerMacroName ) ;
innerMacro - > code ( innerparams , innermacros , innercode ) ;
params2 [ ipar ] = innercode ;
}
}
}
}
return params2 ;
}
2009-01-24 20:28:30 +01:00
public :
2009-09-19 23:09:05 +02:00
/**
2010-03-14 07:48:44 +01:00
* @ brief Constructor for PreprocessorMacro . This is the " setter "
* for this class - everything is setup here .
* @ param macro The code after define , until end of line ,
2009-09-19 23:09:05 +02:00
* e . g . " A(x) foo(x); "
*/
2011-10-28 22:10:19 +02:00
explicit PreprocessorMacro ( const std : : string & macro )
2011-10-13 20:53:06 +02:00
: _macro ( macro ) , _prefix ( " __cppcheck__ " ) {
2010-12-01 18:00:55 +01:00
tokenizer . setSettings ( & settings ) ;
2009-01-24 20:28:30 +01:00
// Tokenize the macro to make it easier to handle
2011-12-08 21:28:34 +01:00
std : : istringstream istr ( macro ) ;
2012-05-05 18:33:26 +02:00
tokenizer . list . createTokens ( istr ) ;
2009-01-24 20:28:30 +01:00
// macro name..
2010-04-02 07:30:58 +02:00
if ( tokens ( ) & & tokens ( ) - > isName ( ) )
2009-01-24 20:28:30 +01:00
_name = tokens ( ) - > str ( ) ;
2010-01-13 21:50:44 +01:00
// initialize parameters to default values
2009-06-19 16:42:47 +02:00
_variadic = _nopar = false ;
2009-02-13 14:31:40 +01:00
2009-01-24 20:28:30 +01:00
std : : string : : size_type pos = macro . find_first_of ( " ( " ) ;
2011-10-13 20:53:06 +02:00
if ( pos ! = std : : string : : npos & & macro [ pos ] = = ' ( ' ) {
2009-01-24 20:28:30 +01:00
// Extract macro parameters
2011-10-13 20:53:06 +02:00
if ( Token : : Match ( tokens ( ) , " %var% ( %var% " ) ) {
for ( const Token * tok = tokens ( ) - > tokAt ( 2 ) ; tok ; tok = tok - > next ( ) ) {
2010-04-02 07:30:58 +02:00
if ( tok - > str ( ) = = " ) " )
2009-01-24 20:28:30 +01:00
break ;
2011-10-13 20:53:06 +02:00
if ( Token : : simpleMatch ( tok , " . . . ) " ) ) {
2011-01-02 11:19:37 +01:00
if ( tok - > previous ( ) - > str ( ) = = " , " )
_params . push_back ( " __VA_ARGS__ " ) ;
2009-02-13 14:31:40 +01:00
_variadic = true ;
break ;
}
2010-04-02 07:30:58 +02:00
if ( tok - > isName ( ) )
2009-01-24 20:28:30 +01:00
_params . push_back ( tok - > str ( ) ) ;
}
}
2009-06-18 23:09:11 +02:00
2010-04-02 07:30:58 +02:00
else if ( Token : : Match ( tokens ( ) , " %var% ( . . . ) " ) )
2009-06-18 23:09:11 +02:00
_variadic = true ;
2009-06-19 16:42:47 +02:00
2010-04-02 07:30:58 +02:00
else if ( Token : : Match ( tokens ( ) , " %var% ( ) " ) )
2009-06-19 16:42:47 +02:00
_nopar = true ;
2009-01-24 20:28:30 +01:00
}
}
2010-01-13 21:50:44 +01:00
/** return tokens of this macro */
2011-10-13 20:53:06 +02:00
const Token * tokens ( ) const {
2009-01-24 20:28:30 +01:00
return tokenizer . tokens ( ) ;
}
2010-01-13 21:50:44 +01:00
/** read parameters of this macro */
2011-10-13 20:53:06 +02:00
const std : : vector < std : : string > & params ( ) const {
2009-01-24 20:28:30 +01:00
return _params ;
}
2010-01-13 21:50:44 +01:00
/** check if this is macro has a variable number of parameters */
2011-10-13 20:53:06 +02:00
bool variadic ( ) const {
2009-02-13 14:31:40 +01:00
return _variadic ;
}
2010-12-15 18:45:53 +01:00
/** Check if this macro has parentheses but no parameters */
2011-10-13 20:53:06 +02:00
bool nopar ( ) const {
2009-06-19 16:42:47 +02:00
return _nopar ;
}
2010-01-13 21:50:44 +01:00
/** name of macro */
2011-10-13 20:53:06 +02:00
const std : : string & name ( ) const {
2009-01-24 20:28:30 +01:00
return _name ;
}
2010-01-13 21:50:44 +01:00
/**
* get expanded code for this macro
* @ param params2 macro parameters
2010-06-19 12:03:39 +02:00
* @ param macros macro definitions ( recursion )
2010-01-13 21:50:44 +01:00
* @ param macrocode output string
* @ return true if the expanding was successful
*/
2011-10-13 20:53:06 +02:00
bool code ( const std : : vector < std : : string > & params2 , const std : : map < std : : string , PreprocessorMacro * > & macros , std : : string & macrocode ) const {
if ( _nopar | | ( _params . empty ( ) & & _variadic ) ) {
2009-06-19 16:42:47 +02:00
macrocode = _macro . substr ( 1 + _macro . find ( " ) " ) ) ;
2010-04-02 07:30:58 +02:00
if ( macrocode . empty ( ) )
2009-06-19 16:42:47 +02:00
return true ;
std : : string : : size_type pos = 0 ;
// Remove leading spaces
2010-04-02 07:30:58 +02:00
if ( ( pos = macrocode . find_first_not_of ( " " ) ) > 0 )
2009-06-19 16:42:47 +02:00
macrocode . erase ( 0 , pos ) ;
// Remove ending newline
2010-04-02 07:30:58 +02:00
if ( ( pos = macrocode . find_first_of ( " \r \n " ) ) ! = std : : string : : npos )
2009-06-19 16:42:47 +02:00
macrocode . erase ( pos ) ;
2009-06-18 23:09:11 +02:00
// Replace "__VA_ARGS__" with parameters
2011-10-13 20:53:06 +02:00
if ( ! _nopar ) {
2010-06-20 18:41:45 +02:00
std : : string s ;
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < params2 . size ( ) ; + + i ) {
2010-06-20 18:41:45 +02:00
if ( i > 0 )
s + = " , " ;
s + = params2 [ i ] ;
}
pos = 0 ;
2011-10-13 20:53:06 +02:00
while ( ( pos = macrocode . find ( " __VA_ARGS__ " , pos ) ) ! = std : : string : : npos ) {
2010-06-20 18:41:45 +02:00
macrocode . erase ( pos , 11 ) ;
macrocode . insert ( pos , s ) ;
pos + = s . length ( ) ;
}
2009-06-18 23:09:11 +02:00
}
}
2011-10-13 20:53:06 +02:00
else if ( _params . empty ( ) ) {
2011-02-11 18:01:27 +01:00
std : : string : : size_type pos = _macro . find_first_of ( " \" " ) ;
2010-04-02 07:30:58 +02:00
if ( pos = = std : : string : : npos )
2009-01-24 20:28:30 +01:00
macrocode = " " ;
2011-10-13 20:53:06 +02:00
else {
2011-02-11 18:01:27 +01:00
if ( _macro [ pos ] = = ' ' )
pos + + ;
macrocode = _macro . substr ( pos ) ;
2010-04-02 07:30:58 +02:00
if ( ( pos = macrocode . find_first_of ( " \r \n " ) ) ! = std : : string : : npos )
2009-01-24 20:28:30 +01:00
macrocode . erase ( pos ) ;
}
}
2011-10-13 20:53:06 +02:00
else {
2010-06-19 12:03:39 +02:00
const std : : vector < std : : string > givenparams = expandInnerMacros ( params2 , macros ) ;
2009-01-24 20:28:30 +01:00
const Token * tok = tokens ( ) ;
2010-04-02 07:30:58 +02:00
while ( tok & & tok - > str ( ) ! = " ) " )
2009-01-24 20:28:30 +01:00
tok = tok - > next ( ) ;
2011-10-13 20:53:06 +02:00
if ( tok ) {
2009-02-13 14:34:24 +01:00
bool optcomma = false ;
2014-02-15 08:37:57 +01:00
while ( nullptr ! = ( tok = tok - > next ( ) ) ) {
2009-01-24 20:28:30 +01:00
std : : string str = tok - > str ( ) ;
2010-04-02 07:30:58 +02:00
if ( str = = " ## " )
2009-01-24 20:28:30 +01:00
continue ;
2011-10-13 20:53:06 +02:00
if ( str [ 0 ] = = ' # ' | | tok - > isName ( ) ) {
2009-10-14 20:40:17 +02:00
const bool stringify ( str [ 0 ] = = ' # ' ) ;
2011-10-13 20:53:06 +02:00
if ( stringify ) {
2009-01-25 14:30:15 +01:00
str = str . erase ( 0 , 1 ) ;
}
2011-10-13 20:53:06 +02:00
for ( unsigned int i = 0 ; i < _params . size ( ) ; + + i ) {
if ( str = = _params [ i ] ) {
2010-04-02 07:30:58 +02:00
if ( _variadic & &
( i = = _params . size ( ) - 1 | |
2011-10-13 20:53:06 +02:00
( givenparams . size ( ) + 2 = = _params . size ( ) & & i + 1 = = _params . size ( ) - 1 ) ) ) {
2009-02-13 14:31:40 +01:00
str = " " ;
2011-10-13 20:53:06 +02:00
for ( unsigned int j = ( unsigned int ) _params . size ( ) - 1 ; j < givenparams . size ( ) ; + + j ) {
2010-04-02 07:30:58 +02:00
if ( optcomma | | j > _params . size ( ) - 1 )
2009-02-13 14:31:40 +01:00
str + = " , " ;
2009-02-13 14:34:24 +01:00
optcomma = false ;
2010-06-19 12:03:39 +02:00
str + = givenparams [ j ] ;
2009-02-13 14:31:40 +01:00
}
2011-10-13 20:53:06 +02:00
} else if ( i > = givenparams . size ( ) ) {
2009-05-18 22:32:04 +02:00
// Macro had more parameters than caller used.
macrocode = " " ;
return false ;
2011-10-13 20:53:06 +02:00
} else if ( stringify ) {
2010-06-19 12:03:39 +02:00
const std : : string & s ( givenparams [ i ] ) ;
2009-10-14 20:40:17 +02:00
std : : ostringstream ostr ;
ostr < < " \" " ;
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type j = 0 ; j < s . size ( ) ; + + j ) {
2010-04-09 21:40:37 +02:00
if ( s [ j ] = = ' \\ ' | | s [ j ] = = ' \" ' )
2009-10-14 20:40:17 +02:00
ostr < < ' \\ ' ;
2010-04-09 21:40:37 +02:00
ostr < < s [ j ] ;
2009-10-14 20:40:17 +02:00
}
str = ostr . str ( ) + " \" " ;
2011-10-13 20:53:06 +02:00
} else
2010-06-19 12:03:39 +02:00
str = givenparams [ i ] ;
2009-05-18 22:32:04 +02:00
2009-01-24 20:28:30 +01:00
break ;
}
}
2010-09-18 22:20:01 +02:00
// expand nopar macro
2011-10-13 20:53:06 +02:00
if ( tok - > strAt ( - 1 ) ! = " ## " ) {
2011-04-07 16:53:42 +02:00
const std : : map < std : : string , PreprocessorMacro * > : : const_iterator it = macros . find ( str ) ;
2011-10-13 20:53:06 +02:00
if ( it ! = macros . end ( ) & & it - > second - > _macro . find ( " ( " ) = = std : : string : : npos ) {
2011-04-07 16:53:42 +02:00
str = it - > second - > _macro ;
if ( str . find ( " " ) ! = std : : string : : npos )
str . erase ( 0 , str . find ( " " ) ) ;
else
str = " " ;
}
2010-09-18 22:20:01 +02:00
}
2009-01-24 20:28:30 +01:00
}
2011-10-13 20:53:06 +02:00
if ( _variadic & & tok - > str ( ) = = " , " & & tok - > next ( ) & & tok - > next ( ) - > str ( ) = = " ## " ) {
2009-02-13 14:34:24 +01:00
optcomma = true ;
continue ;
}
optcomma = false ;
2009-01-24 20:28:30 +01:00
macrocode + = str ;
2010-04-02 07:30:58 +02:00
if ( Token : : Match ( tok , " %var% %var% " ) | |
2010-04-15 22:21:00 +02:00
Token : : Match ( tok , " %var% %num% " ) | |
Token : : Match ( tok , " %num% %var% " ) | |
2011-02-02 16:48:00 +01:00
Token : : simpleMatch ( tok , " > > " ) )
2009-01-24 20:28:30 +01:00
macrocode + = " " ;
}
}
}
2009-05-18 22:32:04 +02:00
return true ;
2009-01-24 20:28:30 +01:00
}
} ;
2010-01-13 21:50:44 +01:00
/**
* Get data from a input string . This is an extended version of std : : getline .
* The std : : getline only get a single line at a time . It can therefore happen that it
* contains a partial statement . This function ensures that the returned data
* doesn ' t end in the middle of a statement . The " getlines " name indicate that
* this function will return multiple lines if needed .
* @ param istr input stream
* @ param line output data
* @ return success
*/
2009-12-09 17:13:48 +01:00
static bool getlines ( std : : istream & istr , std : : string & line )
{
2010-04-02 07:30:58 +02:00
if ( ! istr . good ( ) )
2009-12-09 17:13:48 +01:00
return false ;
line = " " ;
2010-01-02 20:54:52 +01:00
int parlevel = 0 ;
2011-10-13 20:53:06 +02:00
for ( char ch = ( char ) istr . get ( ) ; istr . good ( ) ; ch = ( char ) istr . get ( ) ) {
if ( ch = = ' \' ' | | ch = = ' \" ' ) {
2009-12-09 17:13:48 +01:00
line + = ch ;
2010-08-06 19:38:21 +02:00
char c = 0 ;
2011-10-13 20:53:06 +02:00
while ( istr . good ( ) & & c ! = ch ) {
if ( c = = ' \\ ' ) {
2010-08-07 20:33:24 +02:00
c = ( char ) istr . get ( ) ;
2010-04-02 07:30:58 +02:00
if ( ! istr . good ( ) )
2009-12-09 17:13:48 +01:00
return true ;
line + = c ;
2009-11-18 23:34:00 +01:00
}
2009-12-06 20:10:10 +01:00
2010-08-07 20:33:24 +02:00
c = ( char ) istr . get ( ) ;
2010-04-02 07:30:58 +02:00
if ( ! istr . good ( ) )
2009-12-09 17:13:48 +01:00
return true ;
2010-04-02 07:30:58 +02:00
if ( c = = ' \n ' & & line . compare ( 0 , 1 , " # " ) = = 0 )
2009-12-09 17:13:48 +01:00
return true ;
line + = c ;
}
2009-12-06 20:10:10 +01:00
continue ;
2009-01-24 20:28:30 +01:00
}
2010-04-02 07:30:58 +02:00
if ( ch = = ' ( ' )
2010-01-02 20:54:52 +01:00
+ + parlevel ;
2010-04-02 07:30:58 +02:00
else if ( ch = = ' ) ' )
2010-01-02 20:54:52 +01:00
- - parlevel ;
2011-10-13 20:53:06 +02:00
else if ( ch = = ' \n ' ) {
2010-04-02 07:30:58 +02:00
if ( line . compare ( 0 , 1 , " # " ) = = 0 )
2009-12-09 17:13:48 +01:00
return true ;
2009-01-24 20:28:30 +01:00
2011-10-13 20:53:06 +02:00
if ( istr . peek ( ) = = ' # ' ) {
2009-12-09 17:13:48 +01:00
line + = ch ;
return true ;
}
2011-10-13 20:53:06 +02:00
} else if ( line . compare ( 0 , 1 , " # " ) ! = 0 & & parlevel < = 0 & & ch = = ' ; ' ) {
2009-12-09 17:13:48 +01:00
line + = " ; " ;
return true ;
2009-12-06 20:10:10 +01:00
}
2009-12-06 19:17:59 +01:00
2009-12-09 17:13:48 +01:00
line + = ch ;
}
return true ;
}
2009-01-24 20:28:30 +01:00
2012-07-10 20:29:04 +02:00
bool Preprocessor : : validateCfg ( const std : : string & code , const std : : string & cfg )
{
// fill up "macros" with empty configuration macros
std : : set < std : : string > macros ;
2012-07-13 11:36:02 +02:00
for ( std : : string : : size_type pos = 0 ; pos < cfg . size ( ) ; ) {
2012-07-10 20:29:04 +02:00
const std : : string : : size_type pos2 = cfg . find_first_of ( " ;= " , pos ) ;
if ( pos2 = = std : : string : : npos ) {
macros . insert ( cfg . substr ( pos ) ) ;
break ;
}
if ( cfg [ pos2 ] = = ' ; ' )
macros . insert ( cfg . substr ( pos , pos2 - pos ) ) ;
pos = cfg . find ( " ; " , pos2 ) ;
2012-07-13 11:36:02 +02:00
if ( pos ! = std : : string : : npos )
+ + pos ;
2012-07-10 20:29:04 +02:00
}
// check if any empty macros are used in code
for ( std : : set < std : : string > : : const_iterator it = macros . begin ( ) ; it ! = macros . end ( ) ; + + it ) {
const std : : string & macro = * it ;
2012-07-11 18:29:33 +02:00
std : : string : : size_type pos = 0 ;
2012-07-11 21:20:31 +02:00
while ( ( pos = code . find_first_of ( std : : string ( " # \" ' " ) + macro [ 0 ] , pos ) ) ! = std : : string : : npos ) {
2012-07-11 18:29:33 +02:00
const std : : string : : size_type pos1 = pos ;
const std : : string : : size_type pos2 = pos + macro . size ( ) ;
pos + + ;
// skip string..
if ( code [ pos1 ] = = ' \" ' | | code [ pos1 ] = = ' \' ' ) {
while ( pos < code . size ( ) & & code [ pos ] ! = code [ pos1 ] ) {
if ( code [ pos ] = = ' \\ ' )
+ + pos ;
+ + pos ;
}
+ + pos ;
}
2012-07-11 21:20:31 +02:00
// skip preprocessor statement..
else if ( code [ pos1 ] = = ' # ' ) {
if ( pos1 = = 0 | | code [ pos1 - 1 ] = = ' \n ' )
pos = code . find ( " \n " , pos ) ;
}
2012-07-11 18:29:33 +02:00
// is macro used in code?
else if ( code . compare ( pos1 , macro . size ( ) , macro ) = = 0 ) {
2014-03-18 21:41:47 +01:00
if ( pos1 > 0 & & ( std : : isalnum ( ( unsigned char ) code [ pos1 - 1U ] ) | | code [ pos1 - 1U ] = = ' _ ' ) )
2012-07-11 18:29:33 +02:00
continue ;
2014-03-18 21:41:47 +01:00
if ( pos2 < code . size ( ) & & ( std : : isalnum ( ( unsigned char ) code [ pos2 ] ) | | code [ pos2 ] = = ' _ ' ) )
2012-07-11 18:29:33 +02:00
continue ;
// macro is used in code, return false
if ( _settings - > isEnabled ( " information " ) )
2013-06-06 12:44:19 +02:00
validateCfgError ( cfg , macro ) ;
2012-07-11 18:29:33 +02:00
return false ;
}
2012-07-10 20:29:04 +02:00
}
}
return true ;
}
2013-06-06 12:44:19 +02:00
void Preprocessor : : validateCfgError ( const std : : string & cfg , const std : : string & macro )
2012-07-10 20:29:04 +02:00
{
const std : : string id = " ConfigurationNotChecked " ;
2012-11-03 11:25:40 +01:00
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
ErrorLogger : : ErrorMessage : : FileLocation loc ;
loc . line = 1 ;
loc . setfile ( file0 ) ;
locationList . push_back ( loc ) ;
2013-06-26 09:47:02 +02:00
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 ) ;
2012-07-10 20:29:04 +02:00
_errorLogger - > reportInfo ( errmsg ) ;
}
2012-02-05 20:48:28 +01:00
std : : string Preprocessor : : expandMacros ( const std : : string & code , std : : string filename , const std : : string & cfg , ErrorLogger * errorLogger )
2009-12-09 17:13:48 +01:00
{
// Search for macros and expand them..
2010-01-13 21:50:44 +01:00
// --------------------------------------------
2009-12-06 19:17:59 +01:00
2010-01-13 21:50:44 +01:00
// Available macros (key=macroname, value=macro).
2009-12-09 17:13:48 +01:00
std : : map < std : : string , PreprocessorMacro * > macros ;
2009-01-24 20:28:30 +01:00
2012-02-05 20:48:28 +01:00
{
// fill up "macros" with user defined macros
2014-02-15 08:37:57 +01:00
const std : : map < std : : string , std : : string > cfgmap ( getcfgmap ( cfg , nullptr , " " ) ) ;
2012-02-05 20:48:28 +01:00
std : : map < std : : string , std : : string > : : const_iterator it ;
for ( it = cfgmap . begin ( ) ; it ! = cfgmap . end ( ) ; + + it ) {
std : : string s = it - > first ;
if ( ! it - > second . empty ( ) )
s + = " " + it - > second ;
PreprocessorMacro * macro = new PreprocessorMacro ( s ) ;
macros [ it - > first ] = macro ;
}
}
2010-01-13 21:50:44 +01:00
// Current line number
2009-12-09 17:13:48 +01:00
unsigned int linenr = 1 ;
2009-01-24 20:28:30 +01:00
2009-12-09 17:13:48 +01:00
// linenr, filename
std : : stack < std : : pair < unsigned int , std : : string > > fileinfo ;
2009-12-06 19:17:59 +01:00
2010-01-13 21:50:44 +01:00
// output stream
2009-12-09 17:13:48 +01:00
std : : ostringstream ostr ;
2010-01-13 21:50:44 +01:00
// read code..
2011-12-08 21:28:34 +01:00
std : : istringstream istr ( code ) ;
2009-12-09 17:13:48 +01:00
std : : string line ;
2011-10-13 20:53:06 +02:00
while ( getlines ( istr , line ) ) {
2010-01-13 21:50:44 +01:00
// defining a macro..
2011-10-13 20:53:06 +02:00
if ( line . compare ( 0 , 8 , " #define " ) = = 0 ) {
2009-12-09 17:13:48 +01:00
PreprocessorMacro * macro = new PreprocessorMacro ( line . substr ( 8 ) ) ;
2013-01-13 15:01:31 +01:00
if ( macro - > name ( ) . empty ( ) | | macro - > name ( ) = = " NULL " ) {
2009-12-09 17:13:48 +01:00
delete macro ;
2011-10-23 13:07:43 +02:00
} else if ( macro - > name ( ) = = " BOOST_FOREACH " ) {
// BOOST_FOREACH is currently too complex to parse, so skip it.
delete macro ;
} else {
2009-12-28 19:48:30 +01:00
std : : map < std : : string , PreprocessorMacro * > : : iterator it ;
it = macros . find ( macro - > name ( ) ) ;
2010-04-02 07:30:58 +02:00
if ( it ! = macros . end ( ) )
2009-12-28 19:48:30 +01:00
delete it - > second ;
2009-12-09 17:13:48 +01:00
macros [ macro - > name ( ) ] = macro ;
2009-12-28 19:48:30 +01:00
}
2009-12-09 17:13:48 +01:00
line = " \n " ;
}
2009-01-24 20:28:30 +01:00
2010-01-13 21:50:44 +01:00
// undefining a macro..
2011-10-13 20:53:06 +02:00
else if ( line . compare ( 0 , 7 , " #undef " ) = = 0 ) {
2009-12-09 17:13:48 +01:00
std : : map < std : : string , PreprocessorMacro * > : : iterator it ;
it = macros . find ( line . substr ( 7 ) ) ;
2011-10-13 20:53:06 +02:00
if ( it ! = macros . end ( ) ) {
2009-12-09 17:13:48 +01:00
delete it - > second ;
macros . erase ( it ) ;
2009-12-06 20:10:10 +01:00
}
2009-12-09 17:13:48 +01:00
line = " \n " ;
}
2009-01-24 20:28:30 +01:00
2010-01-13 21:50:44 +01:00
// entering a file, update position..
2011-10-13 20:53:06 +02:00
else if ( line . compare ( 0 , 7 , " #file \" " ) = = 0 ) {
2009-12-09 17:13:48 +01:00
fileinfo . push ( std : : pair < unsigned int , std : : string > ( linenr , filename ) ) ;
filename = line . substr ( 7 , line . length ( ) - 8 ) ;
linenr = 0 ;
line + = " \n " ;
}
2009-09-19 23:09:05 +02:00
2010-01-13 21:50:44 +01:00
// leaving a file, update position..
2011-10-13 20:53:06 +02:00
else if ( line = = " #endfile " ) {
if ( ! fileinfo . empty ( ) ) {
2009-12-09 17:13:48 +01:00
linenr = fileinfo . top ( ) . first ;
filename = fileinfo . top ( ) . second ;
fileinfo . pop ( ) ;
2009-12-06 20:10:10 +01:00
}
2009-12-09 17:13:48 +01:00
line + = " \n " ;
}
2009-11-18 23:34:00 +01:00
2010-01-13 21:50:44 +01:00
// all other preprocessor directives are just replaced with a newline
2011-10-13 20:53:06 +02:00
else if ( line . compare ( 0 , 1 , " # " ) = = 0 ) {
2009-12-09 17:13:48 +01:00
line + = " \n " ;
}
2009-09-19 23:09:05 +02:00
2009-12-09 17:13:48 +01:00
// expand macros..
2011-10-13 20:53:06 +02:00
else {
2009-12-31 12:45:17 +01:00
// Limit for each macro.
// The limit specify a position in the "line" variable.
// For a "recursive macro" where the expanded text contains
// the macro again, the macro should not be expanded again.
// The limits are used to prevent recursive expanding.
// * When a macro is expanded its limit position is set to
// the last expanded character.
// * macros are only allowed to be expanded when the
// the position is beyond the limit.
// * The limit is relative to the end of the "line"
// variable. Inserting and deleting text before the limit
// without updating the limit is safe.
// * when pos goes beyond a limit the limit needs to be
// deleted because it is unsafe to insert/delete text
2010-01-13 21:50:44 +01:00
// after the limit otherwise
2012-07-08 23:39:46 +02:00
std : : map < const PreprocessorMacro * , std : : size_t > limits ;
2009-03-09 20:29:25 +01:00
2009-12-31 12:45:17 +01:00
// pos is the current position in line
2009-12-09 17:13:48 +01:00
std : : string : : size_type pos = 0 ;
2010-01-13 21:50:44 +01:00
// scan line to see if there are any macros to expand..
2010-02-26 22:11:23 +01:00
unsigned int tmpLinenr = 0 ;
2011-10-13 20:53:06 +02:00
while ( pos < line . size ( ) ) {
2010-04-02 07:30:58 +02:00
if ( line [ pos ] = = ' \n ' )
2010-02-26 22:11:23 +01:00
+ + tmpLinenr ;
2009-12-09 17:13:48 +01:00
// skip strings..
2011-10-13 20:53:06 +02:00
if ( line [ pos ] = = ' \" ' | | line [ pos ] = = ' \' ' ) {
2009-12-09 17:13:48 +01:00
const char ch = line [ pos ] ;
skipstring ( line , pos ) ;
+ + pos ;
2011-10-13 20:53:06 +02:00
if ( pos > = line . size ( ) ) {
2009-12-09 17:13:48 +01:00
writeError ( filename ,
2010-02-26 22:11:23 +01:00
linenr + tmpLinenr ,
2009-12-09 17:13:48 +01:00
errorLogger ,
" noQuoteCharPair " ,
std : : string ( " No pair for character ( " ) + ch + " ). Can't process file. File is either invalid or unicode, which is currently not supported. " ) ;
2009-12-28 19:48:30 +01:00
std : : map < std : : string , PreprocessorMacro * > : : iterator it ;
2010-04-02 07:30:58 +02:00
for ( it = macros . begin ( ) ; it ! = macros . end ( ) ; + + it )
2009-12-28 19:48:30 +01:00
delete it - > second ;
2012-01-19 12:41:14 +01:00
macros . clear ( ) ;
2009-12-09 17:13:48 +01:00
return " " ;
}
2009-12-06 20:10:10 +01:00
continue ;
}
2009-05-24 22:57:12 +02:00
2014-03-18 21:41:47 +01:00
if ( ! std : : isalpha ( ( unsigned char ) line [ pos ] ) & & line [ pos ] ! = ' _ ' )
2009-12-09 17:13:48 +01:00
+ + pos ;
2009-01-24 20:28:30 +01:00
2009-12-09 17:13:48 +01:00
// found an identifier..
2010-01-13 21:50:44 +01:00
// the "while" is used in case the expanded macro will immediately call another macro
2014-03-18 21:41:47 +01:00
while ( pos < line . length ( ) & & ( std : : isalpha ( ( unsigned char ) line [ pos ] ) | | line [ pos ] = = ' _ ' ) ) {
2010-01-13 21:50:44 +01:00
// pos1 = start position of macro
2009-12-09 17:13:48 +01:00
const std : : string : : size_type pos1 = pos + + ;
2010-01-13 21:50:44 +01:00
// find the end of the identifier
2014-03-18 21:41:47 +01:00
while ( pos < line . size ( ) & & ( std : : isalnum ( ( unsigned char ) line [ pos ] ) | | line [ pos ] = = ' _ ' ) )
2009-12-09 17:13:48 +01:00
+ + pos ;
2009-12-06 19:17:59 +01:00
2010-01-13 21:50:44 +01:00
// get identifier
2009-12-09 17:13:48 +01:00
const std : : string id = line . substr ( pos1 , pos - pos1 ) ;
2009-12-06 19:17:59 +01:00
2009-12-09 17:13:48 +01:00
// is there a macro with this name?
std : : map < std : : string , PreprocessorMacro * > : : const_iterator it ;
it = macros . find ( id ) ;
2010-04-02 07:30:58 +02:00
if ( it = = macros . end ( ) )
2010-01-13 21:50:44 +01:00
break ; // no macro with this name exist
2009-12-09 17:13:48 +01:00
const PreprocessorMacro * const macro = it - > second ;
2009-12-06 20:10:10 +01:00
2010-01-13 21:50:44 +01:00
// check that pos is within allowed limits for this
2009-12-31 12:45:17 +01:00
// macro
2009-12-06 20:10:10 +01:00
{
2012-07-08 23:39:46 +02:00
const std : : map < const PreprocessorMacro * , std : : size_t > : : const_iterator it2 = limits . find ( macro ) ;
2010-04-02 07:30:58 +02:00
if ( it2 ! = limits . end ( ) & & pos < = line . length ( ) - it2 - > second )
2009-12-06 20:10:10 +01:00
break ;
}
2009-12-09 17:13:48 +01:00
2010-01-13 21:50:44 +01:00
// get parameters from line..
2009-12-09 17:13:48 +01:00
std : : vector < std : : string > params ;
std : : string : : size_type pos2 = pos ;
2010-04-02 07:30:58 +02:00
if ( macro - > params ( ) . size ( ) & & pos2 > = line . length ( ) )
2009-12-09 17:13:48 +01:00
break ;
2010-01-13 21:50:44 +01:00
// number of newlines within macro use
2009-12-09 17:13:48 +01:00
unsigned int numberOfNewlines = 0 ;
2010-12-15 18:45:53 +01:00
// if the macro has parentheses, get parameters
2011-10-13 20:53:06 +02:00
if ( macro - > variadic ( ) | | macro - > nopar ( ) | | macro - > params ( ) . size ( ) ) {
2013-01-16 15:37:07 +01:00
// is the end parentheses found?
2009-12-09 17:13:48 +01:00
bool endFound = false ;
2010-01-13 21:50:44 +01:00
2010-06-19 09:09:47 +02:00
getparams ( line , pos2 , params , numberOfNewlines , endFound ) ;
2010-01-13 21:50:44 +01:00
// something went wrong so bail out
2010-04-02 07:30:58 +02:00
if ( ! endFound )
2009-01-24 20:28:30 +01:00
break ;
}
2009-11-18 23:34:00 +01:00
2010-01-13 21:50:44 +01:00
// Just an empty parameter => clear
2010-04-02 07:30:58 +02:00
if ( params . size ( ) = = 1 & & params [ 0 ] = = " " )
2009-12-09 17:13:48 +01:00
params . clear ( ) ;
2009-11-18 23:34:00 +01:00
2010-01-13 21:50:44 +01:00
// Check that it's the same number of parameters..
2010-04-02 07:30:58 +02:00
if ( ! macro - > variadic ( ) & & params . size ( ) ! = macro - > params ( ) . size ( ) )
2009-12-09 17:13:48 +01:00
break ;
2009-01-24 20:28:30 +01:00
2009-12-09 17:13:48 +01:00
// Create macro code..
std : : string tempMacro ;
2011-10-13 20:53:06 +02:00
if ( ! macro - > code ( params , macros , tempMacro ) ) {
2009-12-09 17:13:48 +01:00
// Syntax error in code
writeError ( filename ,
2010-02-26 22:11:23 +01:00
linenr + tmpLinenr ,
2009-12-09 17:13:48 +01:00
errorLogger ,
" syntaxError " ,
std : : string ( " Syntax error. Not enough parameters for macro ' " ) + macro - > name ( ) + " '. " ) ;
2009-12-28 19:48:30 +01:00
2010-04-09 21:40:37 +02:00
std : : map < std : : string , PreprocessorMacro * > : : iterator iter ;
for ( iter = macros . begin ( ) ; iter ! = macros . end ( ) ; + + iter )
delete iter - > second ;
2012-01-19 12:41:14 +01:00
macros . clear ( ) ;
2009-12-09 17:13:48 +01:00
return " " ;
}
2009-12-06 20:10:10 +01:00
2010-01-13 21:50:44 +01:00
// make sure number of newlines remain the same..
2010-04-15 22:21:00 +02:00
std : : string macrocode ( std : : string ( numberOfNewlines , ' \n ' ) + tempMacro ) ;
2009-12-06 20:10:10 +01:00
2009-12-09 17:13:48 +01:00
// Insert macro code..
2010-04-02 07:30:58 +02:00
if ( macro - > variadic ( ) | | macro - > nopar ( ) | | ! macro - > params ( ) . empty ( ) )
2009-12-09 17:13:48 +01:00
+ + pos2 ;
2009-12-06 20:10:10 +01:00
2009-12-31 12:45:17 +01:00
// Remove old limits
2012-07-08 23:39:46 +02:00
for ( std : : map < const PreprocessorMacro * , std : : size_t > : : iterator iter = limits . begin ( ) ;
2011-10-13 20:53:06 +02:00
iter ! = limits . end ( ) ; ) {
if ( ( line . length ( ) - pos1 ) < iter - > second ) {
2009-12-14 23:06:05 +01:00
// We have gone past this limit, so just delete it
limits . erase ( iter + + ) ;
2011-10-13 20:53:06 +02:00
} else {
2009-12-14 23:06:05 +01:00
+ + iter ;
}
}
2009-12-06 20:10:10 +01:00
2009-12-31 12:45:17 +01:00
// don't allow this macro to be expanded again before pos2
limits [ macro ] = line . length ( ) - pos2 ;
// erase macro
2009-12-09 17:13:48 +01:00
line . erase ( pos1 , pos2 - pos1 ) ;
2009-12-31 12:45:17 +01:00
2010-04-15 22:21:00 +02:00
// Don't glue this macro into variable or number after it
2014-03-18 21:41:47 +01:00
if ( ! line . empty ( ) & & ( std : : isalnum ( ( unsigned char ) line [ pos1 ] ) | | line [ pos1 ] = = ' _ ' ) )
2010-04-15 22:21:00 +02:00
macrocode . append ( 1 , ' ' ) ;
2013-10-13 17:42:06 +02:00
// insert macrochar before each symbol/nr/operator
bool str = false ;
bool chr = false ;
for ( std : : size_t i = 0U ; i < macrocode . size ( ) ; + + i ) {
if ( macrocode [ i ] = = ' \\ ' ) {
i + + ;
continue ;
} else if ( macrocode [ i ] = = ' \" ' )
str = ! str ;
else if ( macrocode [ i ] = = ' \' ' )
chr = ! chr ;
else if ( str | | chr )
continue ;
2014-01-02 11:21:23 +01:00
else if ( macrocode [ i ] = = ' . ' ) { // 5. / .5
2014-03-18 21:41:47 +01:00
if ( ( i > 0U & & std : : isdigit ( ( unsigned char ) macrocode [ i - 1 ] ) ) | |
( i + 1 < macrocode . size ( ) & & std : : isdigit ( ( unsigned char ) macrocode [ i + 1 ] ) ) ) {
if ( i > 0U & & ! std : : isdigit ( ( unsigned char ) macrocode [ i - 1 ] ) ) {
2014-01-02 11:21:23 +01:00
macrocode . insert ( i , 1U , macroChar ) ;
i + + ;
}
i + + ;
2014-03-18 21:41:47 +01:00
if ( i < macrocode . size ( ) & & std : : isdigit ( ( unsigned char ) macrocode [ i ] ) )
2014-01-02 11:21:23 +01:00
i + + ;
2014-01-08 18:05:14 +01:00
if ( i + 1U < macrocode . size ( ) & &
( macrocode [ i ] = = ' e ' | | macrocode [ i ] = = ' E ' ) & &
( macrocode [ i + 1 ] = = ' + ' | | macrocode [ i + 1 ] = = ' - ' ) ) {
i + = 2 ;
}
2014-01-02 11:21:23 +01:00
}
2014-03-18 21:41:47 +01:00
} else if ( std : : isalnum ( ( unsigned char ) macrocode [ i ] ) | | macrocode [ i ] = = ' _ ' ) {
2013-10-13 17:42:06 +02:00
if ( ( i > 0U ) & &
2014-03-18 21:41:47 +01:00
( ! std : : isalnum ( ( unsigned char ) macrocode [ i - 1 ] ) ) & &
2013-10-13 17:42:06 +02:00
( macrocode [ i - 1 ] ! = ' _ ' ) & &
( macrocode [ i - 1 ] ! = macroChar ) ) {
macrocode . insert ( i , 1U , macroChar ) ;
}
2013-12-31 10:24:14 +01:00
2013-12-30 18:03:24 +01:00
// 1e-7 / 1e+7
2013-12-31 10:24:14 +01:00
if ( i + 3U < macrocode . size ( ) & &
2014-03-18 21:41:47 +01:00
( std : : isdigit ( ( unsigned char ) macrocode [ i ] ) | | macrocode [ i ] = = ' . ' ) & &
2013-12-30 18:03:24 +01:00
( macrocode [ i + 1 ] = = ' e ' | | macrocode [ i + 1 ] = = ' E ' ) & &
( macrocode [ i + 2 ] = = ' - ' | | macrocode [ i + 2 ] = = ' + ' ) & &
2014-03-18 21:41:47 +01:00
std : : isdigit ( ( unsigned char ) macrocode [ i + 3 ] ) ) {
2013-12-31 10:24:14 +01:00
i + = 3U ;
}
// 1.f / 1.e7
if ( i + 2U < macrocode . size ( ) & &
2014-03-18 21:41:47 +01:00
std : : isdigit ( ( unsigned char ) macrocode [ i ] ) & &
2013-12-31 10:24:14 +01:00
macrocode [ i + 1 ] = = ' . ' & &
2014-03-18 21:41:47 +01:00
std : : isalpha ( ( unsigned char ) macrocode [ i + 2 ] ) ) {
2013-12-31 10:24:14 +01:00
i + = 2U ;
if ( i + 2U < macrocode . size ( ) & &
( macrocode [ i + 0 ] = = ' e ' | | macrocode [ i + 0 ] = = ' E ' ) & &
( macrocode [ i + 1 ] = = ' - ' | | macrocode [ i + 1 ] = = ' + ' ) & &
2014-03-18 21:41:47 +01:00
std : : isdigit ( ( unsigned char ) macrocode [ i + 2 ] ) ) {
2013-12-31 10:24:14 +01:00
i + = 2U ;
}
}
2013-10-13 17:42:06 +02:00
}
}
2012-03-27 19:35:41 +02:00
line . insert ( pos1 , macroChar + macrocode ) ;
2009-12-31 12:45:17 +01:00
// position = start position.
2009-12-09 17:13:48 +01:00
pos = pos1 ;
}
2009-05-18 22:32:04 +02:00
}
2009-12-09 17:13:48 +01:00
}
2009-05-18 22:32:04 +02:00
2010-01-13 21:50:44 +01:00
// the line has been processed in various ways. Now add it to the output stream
2009-12-09 17:13:48 +01:00
ostr < < line ;
2010-01-13 21:50:44 +01:00
// update linenr
2011-10-13 20:53:06 +02:00
for ( std : : string : : size_type p = 0 ; p < line . length ( ) ; + + p ) {
2010-04-02 07:30:58 +02:00
if ( line [ p ] = = ' \n ' )
2009-12-09 17:13:48 +01:00
+ + linenr ;
2009-01-24 20:28:30 +01:00
}
}
2011-11-26 21:02:04 +01:00
for ( std : : map < std : : string , PreprocessorMacro * > : : iterator it = macros . begin ( ) ; it ! = macros . end ( ) ; + + it )
delete it - > second ;
2012-01-19 12:41:14 +01:00
macros . clear ( ) ;
2009-01-24 20:28:30 +01:00
2009-12-09 17:13:48 +01:00
return ostr . str ( ) ;
2009-01-24 20:28:30 +01:00
}
2010-12-29 12:43:29 +01:00
void Preprocessor : : getErrorMessages ( ErrorLogger * errorLogger , const Settings * settings )
2010-09-03 13:30:49 +02:00
{
2010-12-29 12:43:29 +01:00
Settings settings2 ( * settings ) ;
Preprocessor preprocessor ( & settings2 , errorLogger ) ;
2013-03-13 19:50:50 +01:00
settings2 . checkConfiguration = true ;
2013-03-13 06:48:33 +01:00
preprocessor . missingInclude ( " " , 1 , " " , UserHeader ) ;
preprocessor . missingInclude ( " " , 1 , " " , SystemHeader ) ;
2013-06-06 12:44:19 +02:00
preprocessor . validateCfgError ( " X " , " X " ) ;
2010-12-29 12:43:29 +01:00
preprocessor . error ( " " , 1 , " #error message " ) ; // #error ..
2010-09-03 13:30:49 +02:00
}