Branch data Line data Source code
1 : : /*
2 : : * Cppcheck - A tool for static C/C++ code analysis
3 : : * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
4 : : *
5 : : * This program is free software: you can redistribute it and/or modify
6 : : * it under the terms of the GNU General Public License as published by
7 : : * the Free Software Foundation, either version 3 of the License, or
8 : : * (at your option) any later version.
9 : : *
10 : : * This program is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : : * GNU General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU General Public License
16 : : * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 : : */
18 : :
19 : : #include "cppcheckexecutor.h"
20 : : #include "cppcheck.h"
21 : : #include "threadexecutor.h"
22 : : #include "preprocessor.h"
23 : : #include "errorlogger.h"
24 : : #include <iostream>
25 : : #include <sstream>
26 : : #include <cstdlib> // EXIT_SUCCESS and EXIT_FAILURE
27 : : #include <cstring>
28 : : #include <algorithm>
29 : : #include <climits>
30 : :
31 : : #include "cmdlineparser.h"
32 : : #include "filelister.h"
33 : : #include "path.h"
34 : : #include "pathmatch.h"
35 : :
36 : 0 : CppCheckExecutor::CppCheckExecutor()
37 [ # # ][ # # ]: 0 : : _settings(0), time1(0), errorlist(false)
38 : : {
39 : 0 : }
40 : :
41 [ # # ][ # # ]: 0 : CppCheckExecutor::~CppCheckExecutor()
42 : : {
43 [ # # ]: 0 : }
44 : :
45 : 0 : bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* const argv[])
46 : : {
47 : 0 : Settings& settings = cppcheck->settings();
48 : 0 : CmdLineParser parser(&settings);
49 [ # # ]: 0 : bool success = parser.ParseFromArgs(argc, argv);
50 : :
51 [ # # ]: 0 : if (success) {
52 [ # # ][ # # ]: 0 : if (parser.GetShowVersion() && !parser.GetShowErrorMessages()) {
[ # # ]
53 [ # # ]: 0 : const char * extraVersion = cppcheck->extraVersion();
54 [ # # ]: 0 : if (*extraVersion != 0)
55 [ # # ][ # # ]: 0 : std::cout << "Cppcheck " << cppcheck->version() << " ("
[ # # ][ # # ]
56 [ # # ][ # # ]: 0 : << extraVersion << ')' << std::endl;
[ # # ]
57 : : else
58 [ # # ][ # # ]: 0 : std::cout << "Cppcheck " << cppcheck->version() << std::endl;
[ # # ][ # # ]
59 : : }
60 : :
61 [ # # ]: 0 : if (parser.GetShowErrorMessages()) {
62 : 0 : errorlist = true;
63 [ # # ][ # # ]: 0 : std::cout << ErrorLogger::ErrorMessage::getXMLHeader(settings._xml_version);
[ # # ]
64 [ # # ]: 0 : cppcheck->getErrorMessages();
65 [ # # ][ # # ]: 0 : std::cout << ErrorLogger::ErrorMessage::getXMLFooter(settings._xml_version) << std::endl;
[ # # ][ # # ]
66 : : }
67 : :
68 [ # # ]: 0 : if (parser.ExitAfterPrinting())
69 : 0 : std::exit(EXIT_SUCCESS);
70 : : } else {
71 : 0 : std::exit(EXIT_FAILURE);
72 : : }
73 : :
74 : : // Check that all include paths exist
75 : : {
76 : 0 : std::list<std::string>::iterator iter;
77 [ # # ][ # # ]: 0 : for (iter = settings._includePaths.begin();
78 [ # # ]: 0 : iter != settings._includePaths.end();
79 : : ) {
80 [ # # ][ # # ]: 0 : const std::string path(Path::toNativeSeparators(*iter));
[ # # ]
81 [ # # ][ # # ]: 0 : if (FileLister::isDirectory(path))
82 : 0 : ++iter;
83 : : else {
84 : : // If the include path is not found, warn user and remove the
85 : : // non-existing path from the list.
86 [ # # ][ # # ]: 0 : std::cout << "cppcheck: warning: Couldn't find path given by -I '" << path << '\'' << std::endl;
[ # # ][ # # ]
87 [ # # ]: 0 : iter = settings._includePaths.erase(iter);
88 : : }
89 [ # # ]: 0 : }
90 : : }
91 : :
92 : 0 : const std::vector<std::string>& pathnames = parser.GetPathNames();
93 : :
94 [ # # ][ # # ]: 0 : if (!pathnames.empty()) {
95 : : // Execute recursiveAddFiles() to each given file parameter
96 : 0 : std::vector<std::string>::const_iterator iter;
97 [ # # ][ # # ]: 0 : for (iter = pathnames.begin(); iter != pathnames.end(); ++iter)
[ # # ][ # # ]
98 [ # # ][ # # ]: 0 : FileLister::recursiveAddFiles(_files, Path::toNativeSeparators(*iter));
[ # # ][ # # ]
[ # # ]
99 : : }
100 : :
101 [ # # ][ # # ]: 0 : if (!_files.empty()) {
102 : : // Remove header files from the list of ignored files.
103 : : // Also output a warning for the user.
104 : : // TODO: Remove all unknown files? (use FileLister::acceptFile())
105 : 0 : bool warn = false;
106 [ # # ]: 0 : std::vector<std::string> ignored = parser.GetIgnoredPaths();
107 [ # # ][ # # ]: 0 : for (std::vector<std::string>::iterator i = ignored.begin(); i != ignored.end();) {
[ # # ][ # # ]
108 [ # # ]: 0 : const std::string extension = Path::getFilenameExtension(*i);
109 [ # # ][ # # ]: 0 : if (extension == ".h" || extension == ".hpp") {
[ # # ][ # # ]
[ # # ]
110 [ # # ]: 0 : i = ignored.erase(i);
111 : 0 : warn = true;
112 : : } else
113 : 0 : ++i;
114 [ # # ]: 0 : }
115 [ # # ]: 0 : if (warn) {
116 [ # # ][ # # ]: 0 : std::cout << "cppcheck: filename exclusion does not apply to header (.h and .hpp) files." << std::endl;
117 [ # # ][ # # ]: 0 : std::cout << "cppcheck: Please use --suppress for ignoring results from the header files." << std::endl;
118 : : }
119 : :
120 : : #if defined(_WIN32)
121 : : // For Windows we want case-insensitive path matching
122 : : const bool caseSensitive = false;
123 : : #else
124 : 0 : const bool caseSensitive = true;
125 : : #endif
126 [ # # ]: 0 : PathMatch matcher(parser.GetIgnoredPaths(), caseSensitive);
127 [ # # ][ # # ]: 0 : for (std::map<std::string, std::size_t>::iterator i = _files.begin(); i != _files.end();) {
[ # # ]
128 [ # # ][ # # ]: 0 : if (matcher.Match(i->first))
[ # # ]
129 [ # # ]: 0 : _files.erase(i++);
130 : : else
131 : 0 : ++i;
132 [ # # ][ # # ]: 0 : }
133 : : } else {
134 [ # # ][ # # ]: 0 : std::cout << "cppcheck: error: could not find or open any of the paths given." << std::endl;
135 : 0 : return false;
136 : : }
137 : :
138 [ # # ][ # # ]: 0 : if (!_files.empty()) {
139 : 0 : return true;
140 : : } else {
141 [ # # ][ # # ]: 0 : std::cout << "cppcheck: error: no files to check - all paths ignored." << std::endl;
142 : 0 : return false;
143 : 0 : }
144 : : }
145 : :
146 : 0 : int CppCheckExecutor::check(int argc, const char* const argv[])
147 : : {
148 : 0 : Preprocessor::missingIncludeFlag = false;
149 : :
150 : 0 : CppCheck cppCheck(*this, true);
151 : :
152 [ # # ]: 0 : Settings& settings = cppCheck.settings();
153 : 0 : _settings = &settings;
154 : :
155 [ # # ][ # # ]: 0 : if (!parseFromArgs(&cppCheck, argc, argv)) {
156 : 0 : return EXIT_FAILURE;
157 : : }
158 : :
159 [ # # ]: 0 : if (settings.reportProgress)
160 : 0 : time1 = std::time(0);
161 : :
162 [ # # ]: 0 : if (settings._xml) {
163 [ # # ][ # # ]: 0 : reportErr(ErrorLogger::ErrorMessage::getXMLHeader(settings._xml_version));
[ # # ]
164 : : }
165 : :
166 : 0 : unsigned int returnValue = 0;
167 [ # # ]: 0 : if (settings._jobs == 1) {
168 : : // Single process
169 : :
170 : 0 : std::size_t totalfilesize = 0;
171 [ # # ][ # # ]: 0 : for (std::map<std::string, std::size_t>::const_iterator i = _files.begin(); i != _files.end(); ++i) {
[ # # ]
172 [ # # ]: 0 : totalfilesize += i->second;
173 : : }
174 : :
175 : 0 : std::size_t processedsize = 0;
176 : 0 : unsigned int c = 0;
177 [ # # ][ # # ]: 0 : for (std::map<std::string, std::size_t>::const_iterator i = _files.begin(); i != _files.end(); ++i) {
[ # # ]
178 [ # # ][ # # ]: 0 : returnValue += cppCheck.check(i->first);
179 [ # # ]: 0 : processedsize += i->second;
180 [ # # ]: 0 : if (!settings._errorsOnly)
181 [ # # ][ # # ]: 0 : reportStatus(c + 1, _files.size(), processedsize, totalfilesize);
182 : 0 : c++;
183 : : }
184 : :
185 [ # # ]: 0 : cppCheck.checkFunctionUsage();
186 [ # # ]: 0 : } else if (!ThreadExecutor::isEnabled()) {
187 [ # # ][ # # ]: 0 : std::cout << "No thread support yet implemented for this platform." << std::endl;
188 : : } else {
189 : : // Multiple processes
190 [ # # ]: 0 : ThreadExecutor executor(_files, settings, *this);
191 [ # # ][ # # ]: 0 : returnValue = executor.check();
192 : : }
193 : :
194 [ # # ][ # # ]: 0 : if (settings.isEnabled("information") || settings.checkConfiguration)
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
195 [ # # ][ # # ]: 0 : reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions());
[ # # ]
196 : :
197 [ # # ]: 0 : if (!settings.checkConfiguration) {
198 [ # # ][ # # ]: 0 : cppCheck.tooManyConfigsError("",0U);
[ # # ]
199 : :
200 [ # # ][ # # ]: 0 : if (settings.isEnabled("missingInclude") && Preprocessor::missingIncludeFlag) {
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
201 [ # # ]: 0 : const std::list<ErrorLogger::ErrorMessage::FileLocation> callStack;
202 : : ErrorLogger::ErrorMessage msg(callStack,
203 : : Severity::information,
204 : : "Cppcheck cannot find all the include files (use --check-config for details)\n"
205 : : "Cppcheck cannot find all the include files. Cppcheck can check the code without the "
206 : : "include files found. But the results will probably be more accurate if all the include "
207 : : "files are found. Please check your project's include directories and add all of them "
208 : : "as include directories for Cppcheck. To see what files Cppcheck cannot find use "
209 : : "--check-config.",
210 : : "missingInclude",
211 [ # # ][ # # ]: 0 : false);
[ # # ][ # # ]
[ # # ]
212 [ # # ][ # # ]: 0 : reportInfo(msg);
[ # # ]
213 : : }
214 : : }
215 : :
216 [ # # ]: 0 : if (settings._xml) {
217 [ # # ][ # # ]: 0 : reportErr(ErrorLogger::ErrorMessage::getXMLFooter(settings._xml_version));
[ # # ]
218 : : }
219 : :
220 : 0 : _settings = 0;
221 [ # # ]: 0 : if (returnValue)
222 : 0 : return settings._exitCode;
223 : : else
224 : 0 : return 0;
225 : : }
226 : :
227 : 0 : void CppCheckExecutor::reportErr(const std::string &errmsg)
228 : : {
229 : : // Alert only about unique errors
230 [ # # ]: 0 : if (_errorList.find(errmsg) != _errorList.end())
231 : 0 : return;
232 : :
233 : 0 : _errorList.insert(errmsg);
234 : 0 : std::cerr << errmsg << std::endl;
235 : : }
236 : :
237 : 0 : void CppCheckExecutor::reportOut(const std::string &outmsg)
238 : : {
239 : 0 : std::cout << outmsg << std::endl;
240 : 0 : }
241 : :
242 : 0 : void CppCheckExecutor::reportProgress(const std::string &filename, const char stage[], const std::size_t value)
243 : : {
244 : : (void)filename;
245 : :
246 [ # # ]: 0 : if (!time1)
247 : 0 : return;
248 : :
249 : : // Report progress messages every 10 seconds
250 : 0 : const std::time_t time2 = std::time(NULL);
251 [ # # ]: 0 : if (time2 >= (time1 + 10)) {
252 : 0 : time1 = time2;
253 : :
254 : : // format a progress message
255 : 0 : std::ostringstream ostr;
256 [ # # ]: 0 : ostr << "progress: "
257 [ # # ]: 0 : << stage
258 [ # # ][ # # ]: 0 : << ' ' << value << '%';
[ # # ]
259 : :
260 : : // Report progress message
261 [ # # ][ # # ]: 0 : reportOut(ostr.str());
[ # # ]
262 : : }
263 : : }
264 : :
265 : 0 : void CppCheckExecutor::reportInfo(const ErrorLogger::ErrorMessage &msg)
266 : : {
267 : 0 : reportErr(msg);
268 : 0 : }
269 : :
270 : 966 : void CppCheckExecutor::reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal)
271 : : {
272 [ + + ]: 966 : if (filecount > 1) {
273 : 396 : std::ostringstream oss;
274 [ + - ][ + - ]: 396 : oss << fileindex << '/' << filecount
[ + - ]
275 [ + - ]: 396 : << " files checked " <<
276 [ + - ][ + - ]: 792 : (sizetotal > 0 ? static_cast<long>(static_cast<long double>(sizedone) / sizetotal*100) : 0)
277 [ + - ]: 396 : << "% done";
278 [ + - ][ + - ]: 396 : std::cout << oss.str() << std::endl;
[ + - ][ + - ]
279 : : }
280 : 966 : }
281 : :
282 : 0 : void CppCheckExecutor::reportErr(const ErrorLogger::ErrorMessage &msg)
283 : : {
284 [ # # ]: 0 : if (errorlist) {
285 [ # # ]: 0 : reportOut(msg.toXML(false, _settings->_xml_version));
286 [ # # ]: 0 : } else if (_settings->_xml) {
287 [ # # ]: 0 : reportErr(msg.toXML(_settings->_verbose, _settings->_xml_version));
288 : : } else {
289 [ # # ]: 0 : reportErr(msg.toString(_settings->_verbose, _settings->_outputFormat));
290 : : }
291 [ + - ][ + - ]: 135 : }
|