|
Cppcheck
|
00001 /* 00002 * Cppcheck - A tool for static C/C++ code analysis 00003 * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team. 00004 * 00005 * This program is free software: you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation, either version 3 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 #include "cppcheckexecutor.h" 00020 #include "cppcheck.h" 00021 #include "threadexecutor.h" 00022 #include "preprocessor.h" 00023 #include "errorlogger.h" 00024 #include <iostream> 00025 #include <sstream> 00026 #include <cstdlib> // EXIT_SUCCESS and EXIT_FAILURE 00027 #include <cstring> 00028 #include <algorithm> 00029 #include <climits> 00030 00031 #include "cmdlineparser.h" 00032 #include "filelister.h" 00033 #include "path.h" 00034 #include "pathmatch.h" 00035 00036 CppCheckExecutor::CppCheckExecutor() 00037 : _settings(0), time1(0), errorlist(false) 00038 { 00039 } 00040 00041 CppCheckExecutor::~CppCheckExecutor() 00042 { 00043 } 00044 00045 bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* const argv[]) 00046 { 00047 Settings& settings = cppcheck->settings(); 00048 CmdLineParser parser(&settings); 00049 bool success = parser.ParseFromArgs(argc, argv); 00050 00051 if (success) { 00052 if (parser.GetShowVersion() && !parser.GetShowErrorMessages()) { 00053 const char * extraVersion = cppcheck->extraVersion(); 00054 if (*extraVersion != 0) 00055 std::cout << "Cppcheck " << cppcheck->version() << " (" 00056 << extraVersion << ')' << std::endl; 00057 else 00058 std::cout << "Cppcheck " << cppcheck->version() << std::endl; 00059 } 00060 00061 if (parser.GetShowErrorMessages()) { 00062 errorlist = true; 00063 std::cout << ErrorLogger::ErrorMessage::getXMLHeader(settings._xml_version); 00064 cppcheck->getErrorMessages(); 00065 std::cout << ErrorLogger::ErrorMessage::getXMLFooter(settings._xml_version) << std::endl; 00066 } 00067 00068 if (parser.ExitAfterPrinting()) 00069 std::exit(EXIT_SUCCESS); 00070 } else { 00071 std::exit(EXIT_FAILURE); 00072 } 00073 00074 // Check that all include paths exist 00075 { 00076 std::list<std::string>::iterator iter; 00077 for (iter = settings._includePaths.begin(); 00078 iter != settings._includePaths.end(); 00079 ) { 00080 const std::string path(Path::toNativeSeparators(*iter)); 00081 if (FileLister::isDirectory(path)) 00082 ++iter; 00083 else { 00084 // If the include path is not found, warn user and remove the 00085 // non-existing path from the list. 00086 std::cout << "cppcheck: warning: Couldn't find path given by -I '" << path << '\'' << std::endl; 00087 iter = settings._includePaths.erase(iter); 00088 } 00089 } 00090 } 00091 00092 const std::vector<std::string>& pathnames = parser.GetPathNames(); 00093 00094 if (!pathnames.empty()) { 00095 // Execute recursiveAddFiles() to each given file parameter 00096 std::vector<std::string>::const_iterator iter; 00097 for (iter = pathnames.begin(); iter != pathnames.end(); ++iter) 00098 FileLister::recursiveAddFiles(_files, Path::toNativeSeparators(*iter)); 00099 } 00100 00101 if (!_files.empty()) { 00102 // Remove header files from the list of ignored files. 00103 // Also output a warning for the user. 00104 // TODO: Remove all unknown files? (use FileLister::acceptFile()) 00105 bool warn = false; 00106 std::vector<std::string> ignored = parser.GetIgnoredPaths(); 00107 for (std::vector<std::string>::iterator i = ignored.begin(); i != ignored.end();) { 00108 const std::string extension = Path::getFilenameExtension(*i); 00109 if (extension == ".h" || extension == ".hpp") { 00110 i = ignored.erase(i); 00111 warn = true; 00112 } else 00113 ++i; 00114 } 00115 if (warn) { 00116 std::cout << "cppcheck: filename exclusion does not apply to header (.h and .hpp) files." << std::endl; 00117 std::cout << "cppcheck: Please use --suppress for ignoring results from the header files." << std::endl; 00118 } 00119 00120 #if defined(_WIN32) 00121 // For Windows we want case-insensitive path matching 00122 const bool caseSensitive = false; 00123 #else 00124 const bool caseSensitive = true; 00125 #endif 00126 PathMatch matcher(parser.GetIgnoredPaths(), caseSensitive); 00127 for (std::map<std::string, std::size_t>::iterator i = _files.begin(); i != _files.end();) { 00128 if (matcher.Match(i->first)) 00129 _files.erase(i++); 00130 else 00131 ++i; 00132 } 00133 } else { 00134 std::cout << "cppcheck: error: could not find or open any of the paths given." << std::endl; 00135 return false; 00136 } 00137 00138 if (!_files.empty()) { 00139 return true; 00140 } else { 00141 std::cout << "cppcheck: error: no files to check - all paths ignored." << std::endl; 00142 return false; 00143 } 00144 } 00145 00146 int CppCheckExecutor::check(int argc, const char* const argv[]) 00147 { 00148 Preprocessor::missingIncludeFlag = false; 00149 00150 CppCheck cppCheck(*this, true); 00151 00152 Settings& settings = cppCheck.settings(); 00153 _settings = &settings; 00154 00155 if (!parseFromArgs(&cppCheck, argc, argv)) { 00156 return EXIT_FAILURE; 00157 } 00158 00159 if (settings.reportProgress) 00160 time1 = std::time(0); 00161 00162 if (settings._xml) { 00163 reportErr(ErrorLogger::ErrorMessage::getXMLHeader(settings._xml_version)); 00164 } 00165 00166 unsigned int returnValue = 0; 00167 if (settings._jobs == 1) { 00168 // Single process 00169 00170 std::size_t totalfilesize = 0; 00171 for (std::map<std::string, std::size_t>::const_iterator i = _files.begin(); i != _files.end(); ++i) { 00172 totalfilesize += i->second; 00173 } 00174 00175 std::size_t processedsize = 0; 00176 unsigned int c = 0; 00177 for (std::map<std::string, std::size_t>::const_iterator i = _files.begin(); i != _files.end(); ++i) { 00178 returnValue += cppCheck.check(i->first); 00179 processedsize += i->second; 00180 if (!settings._errorsOnly) 00181 reportStatus(c + 1, _files.size(), processedsize, totalfilesize); 00182 c++; 00183 } 00184 00185 cppCheck.checkFunctionUsage(); 00186 } else if (!ThreadExecutor::isEnabled()) { 00187 std::cout << "No thread support yet implemented for this platform." << std::endl; 00188 } else { 00189 // Multiple processes 00190 ThreadExecutor executor(_files, settings, *this); 00191 returnValue = executor.check(); 00192 } 00193 00194 if (!settings.checkConfiguration) { 00195 if (!settings._errorsOnly) 00196 reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions()); 00197 00198 cppCheck.tooManyConfigsError("",0U); 00199 00200 if (settings.isEnabled("missingInclude") && Preprocessor::missingIncludeFlag) { 00201 const std::list<ErrorLogger::ErrorMessage::FileLocation> callStack; 00202 ErrorLogger::ErrorMessage msg(callStack, 00203 Severity::information, 00204 "Cppcheck cannot find all the include files (use --check-config for details)\n" 00205 "Cppcheck cannot find all the include files. Cppcheck can check the code without the " 00206 "include files found. But the results will probably be more accurate if all the include " 00207 "files are found. Please check your project's include directories and add all of them " 00208 "as include directories for Cppcheck. To see what files Cppcheck cannot find use " 00209 "--check-config.", 00210 "missingInclude", 00211 false); 00212 reportInfo(msg); 00213 } 00214 } 00215 00216 if (settings._xml) { 00217 reportErr(ErrorLogger::ErrorMessage::getXMLFooter(settings._xml_version)); 00218 } 00219 00220 _settings = 0; 00221 if (returnValue) 00222 return settings._exitCode; 00223 else 00224 return 0; 00225 } 00226 00227 void CppCheckExecutor::reportErr(const std::string &errmsg) 00228 { 00229 // Alert only about unique errors 00230 if (_errorList.find(errmsg) != _errorList.end()) 00231 return; 00232 00233 _errorList.insert(errmsg); 00234 std::cerr << errmsg << std::endl; 00235 } 00236 00237 void CppCheckExecutor::reportOut(const std::string &outmsg) 00238 { 00239 std::cout << outmsg << std::endl; 00240 } 00241 00242 void CppCheckExecutor::reportProgress(const std::string &filename, const char stage[], const std::size_t value) 00243 { 00244 (void)filename; 00245 00246 if (!time1) 00247 return; 00248 00249 // Report progress messages every 10 seconds 00250 const std::time_t time2 = std::time(NULL); 00251 if (time2 >= (time1 + 10)) { 00252 time1 = time2; 00253 00254 // format a progress message 00255 std::ostringstream ostr; 00256 ostr << "progress: " 00257 << stage 00258 << ' ' << value << '%'; 00259 00260 // Report progress message 00261 reportOut(ostr.str()); 00262 } 00263 } 00264 00265 void CppCheckExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg) 00266 { 00267 reportErr(msg); 00268 } 00269 00270 void CppCheckExecutor::reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal) 00271 { 00272 if (filecount > 1) { 00273 std::ostringstream oss; 00274 oss << fileindex << '/' << filecount 00275 << " files checked " << 00276 (sizetotal > 0 ? static_cast<long>(static_cast<long double>(sizedone) / sizetotal*100) : 0) 00277 << "% done"; 00278 std::cout << oss.str() << std::endl; 00279 } 00280 } 00281 00282 void CppCheckExecutor::reportErr(const ErrorLogger::ErrorMessage &msg) 00283 { 00284 if (errorlist) { 00285 reportOut(msg.toXML(false, _settings->_xml_version)); 00286 } else if (_settings->_xml) { 00287 reportErr(msg.toXML(_settings->_verbose, _settings->_xml_version)); 00288 } else { 00289 reportErr(msg.toString(_settings->_verbose, _settings->_outputFormat)); 00290 } 00291 }
1.7.6.1