1 /*
2 * Cppcheck - A tool for static C/C++ code analysis
3 * Copyright (C) 2007-2021 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
21 #include "analyzerinfo.h"
22 #include "cmdlineparser.h"
23 #include "color.h"
24 #include "config.h"
25 #include "cppcheck.h"
26 #include "filelister.h"
27 #include "importproject.h"
28 #include "library.h"
29 #include "path.h"
30 #include "pathmatch.h"
31 #include "preprocessor.h"
32 #include "settings.h"
33 #include "suppressions.h"
34 #include "threadexecutor.h"
35 #include "utils.h"
36 #include "checkunusedfunctions.h"
37
38 #include <csignal>
39 #include <cstdio>
40 #include <cstdlib> // EXIT_SUCCESS and EXIT_FAILURE
41 #include <cstring>
42 #include <iostream>
43 #include <list>
44 #include <memory>
45 #include <utility>
46 #include <vector>
47
48 #if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__OS2__)
49 #define USE_UNIX_SIGNAL_HANDLING
50 #include <unistd.h>
51 #if defined(__APPLE__)
52 # define _XOPEN_SOURCE // ucontext.h APIs can only be used on Mac OSX >= 10.7 if _XOPEN_SOURCE is defined
53 # include <ucontext.h>
54
55 # undef _XOPEN_SOURCE
56 #elif !defined(__OpenBSD__) && !defined(__HAIKU__)
57 # include <ucontext.h>
58 #endif
59 #ifdef __linux__
60 #include <sys/syscall.h>
61 #include <sys/types.h>
62 #endif
63 #endif
64
65 #if !defined(NO_UNIX_BACKTRACE_SUPPORT) && defined(USE_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && defined(__GLIBC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__NetBSD__) && !defined(__SVR4) && !defined(__QNX__)
66 #define USE_UNIX_BACKTRACE_SUPPORT
67 #include <cxxabi.h>
68 #include <execinfo.h>
69 #endif
70
71 #if defined(_WIN32)
72 #if defined(_MSC_VER)
73 #define USE_WINDOWS_SEH
74 #endif
75 #include <Windows.h>
76 #include <DbgHelp.h>
77 #include <TCHAR.H>
78 #include <excpt.h>
79 #endif
80
81
82 /*static*/ FILE* CppCheckExecutor::mExceptionOutput = stdout;
83
CppCheckExecutor()84 CppCheckExecutor::CppCheckExecutor()
85 : mSettings(nullptr), mLatestProgressOutputTime(0), mErrorOutput(nullptr), mBugHuntingReport(nullptr), mShowAllErrors(false)
86 {}
87
~CppCheckExecutor()88 CppCheckExecutor::~CppCheckExecutor()
89 {
90 delete mErrorOutput;
91 delete mBugHuntingReport;
92 }
93
parseFromArgs(CppCheck * cppcheck,int argc,const char * const argv[])94 bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* const argv[])
95 {
96 Settings& settings = cppcheck->settings();
97 CmdLineParser parser(&settings);
98 const bool success = parser.parseFromArgs(argc, argv);
99
100 if (success) {
101 if (parser.getShowVersion() && !parser.getShowErrorMessages()) {
102 const char * const extraVersion = CppCheck::extraVersion();
103 if (*extraVersion != 0)
104 std::cout << "Cppcheck " << CppCheck::version() << " ("
105 << extraVersion << ')' << std::endl;
106 else
107 std::cout << "Cppcheck " << CppCheck::version() << std::endl;
108 }
109
110 if (parser.getShowErrorMessages()) {
111 mShowAllErrors = true;
112 std::cout << ErrorMessage::getXMLHeader();
113 cppcheck->getErrorMessages();
114 std::cout << ErrorMessage::getXMLFooter() << std::endl;
115 }
116
117 if (parser.exitAfterPrinting()) {
118 Settings::terminate();
119 return true;
120 }
121 } else {
122 return false;
123 }
124
125 // Check that all include paths exist
126 {
127 for (std::list<std::string>::iterator iter = settings.includePaths.begin();
128 iter != settings.includePaths.end();
129 ) {
130 const std::string path(Path::toNativeSeparators(*iter));
131 if (FileLister::isDirectory(path))
132 ++iter;
133 else {
134 // If the include path is not found, warn user and remove the non-existing path from the list.
135 if (settings.severity.isEnabled(Severity::information))
136 std::cout << "(information) Couldn't find path given by -I '" << path << '\'' << std::endl;
137 iter = settings.includePaths.erase(iter);
138 }
139 }
140 }
141
142 // Output a warning for the user if he tries to exclude headers
143 bool warn = false;
144 const std::vector<std::string>& ignored = parser.getIgnoredPaths();
145 for (const std::string &i : ignored) {
146 if (Path::isHeader(i)) {
147 warn = true;
148 break;
149 }
150 }
151 if (warn) {
152 std::cout << "cppcheck: filename exclusion does not apply to header (.h and .hpp) files." << std::endl;
153 std::cout << "cppcheck: Please use --suppress for ignoring results from the header files." << std::endl;
154 }
155
156 const std::vector<std::string>& pathnames = parser.getPathNames();
157
158 #if defined(_WIN32)
159 // For Windows we want case-insensitive path matching
160 const bool caseSensitive = false;
161 #else
162 const bool caseSensitive = true;
163 #endif
164 if (!mSettings->project.fileSettings.empty() && !mSettings->fileFilter.empty()) {
165 // filter only for the selected filenames from all project files
166 std::list<ImportProject::FileSettings> newList;
167
168 for (const ImportProject::FileSettings &fsetting : settings.project.fileSettings) {
169 if (matchglob(mSettings->fileFilter, fsetting.filename)) {
170 newList.emplace_back(fsetting);
171 }
172 }
173 if (!newList.empty())
174 settings.project.fileSettings = newList;
175 else {
176 std::cout << "cppcheck: error: could not find any files matching the filter." << std::endl;
177 return false;
178 }
179 } else if (!pathnames.empty()) {
180 // Execute recursiveAddFiles() to each given file parameter
181 const PathMatch matcher(ignored, caseSensitive);
182 for (const std::string &pathname : pathnames) {
183 std::string err = FileLister::recursiveAddFiles(mFiles, Path::toNativeSeparators(pathname), mSettings->library.markupExtensions(), matcher);
184 if (!err.empty()) {
185 std::cout << "cppcheck: " << err << std::endl;
186 }
187 }
188 }
189
190 if (mFiles.empty() && settings.project.fileSettings.empty()) {
191 std::cout << "cppcheck: error: could not find or open any of the paths given." << std::endl;
192 if (!ignored.empty())
193 std::cout << "cppcheck: Maybe all paths were ignored?" << std::endl;
194 return false;
195 } else if (!mSettings->fileFilter.empty() && settings.project.fileSettings.empty()) {
196 std::map<std::string, std::size_t> newMap;
197 for (std::map<std::string, std::size_t>::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i)
198 if (matchglob(mSettings->fileFilter, i->first)) {
199 newMap[i->first] = i->second;
200 }
201 mFiles = newMap;
202 if (mFiles.empty()) {
203 std::cout << "cppcheck: error: could not find any files matching the filter." << std::endl;
204 return false;
205 }
206
207 }
208
209 return true;
210 }
211
check(int argc,const char * const argv[])212 int CppCheckExecutor::check(int argc, const char* const argv[])
213 {
214 Preprocessor::missingIncludeFlag = false;
215 Preprocessor::missingSystemIncludeFlag = false;
216
217 CheckUnusedFunctions::clear();
218
219 CppCheck cppCheck(*this, true, executeCommand);
220
221 const Settings& settings = cppCheck.settings();
222 mSettings = &settings;
223
224 if (!parseFromArgs(&cppCheck, argc, argv)) {
225 mSettings = nullptr;
226 return EXIT_FAILURE;
227 }
228 if (Settings::terminated()) {
229 mSettings = nullptr;
230 return EXIT_SUCCESS;
231 }
232
233 int ret;
234
235 if (cppCheck.settings().exceptionHandling)
236 ret = check_wrapper(cppCheck, argc, argv);
237 else
238 ret = check_internal(cppCheck, argc, argv);
239
240 mSettings = nullptr;
241 return ret;
242 }
243
setSettings(const Settings & settings)244 void CppCheckExecutor::setSettings(const Settings &settings)
245 {
246 mSettings = &settings;
247 }
248
249 /**
250 * Simple helper function:
251 * \return size of array
252 * */
253 template<typename T, int size>
getArrayLength(const T (&)[size])254 std::size_t getArrayLength(const T (&)[size])
255 {
256 return size;
257 }
258
259
260 #if defined(USE_UNIX_SIGNAL_HANDLING)
261 /*
262 * Try to print the callstack.
263 * That is very sensitive to the operating system, hardware, compiler and runtime.
264 * The code is not meant for production environment!
265 * One reason is named first: it's using functions not whitelisted for usage in a signal handler function.
266 */
print_stacktrace(FILE * output,bool demangling,int maxdepth,bool lowMem)267 static void print_stacktrace(FILE* output, bool demangling, int maxdepth, bool lowMem)
268 {
269 #if defined(USE_UNIX_BACKTRACE_SUPPORT)
270 // 32 vs. 64bit
271 #define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8)
272 const int fd = fileno(output);
273 void *callstackArray[32]= {nullptr}; // the less resources the better...
274 const int currentdepth = backtrace(callstackArray, (int)getArrayLength(callstackArray));
275 const int offset=2; // some entries on top are within our own exception handling code or libc
276 if (maxdepth<0)
277 maxdepth=currentdepth-offset;
278 else
279 maxdepth = std::min(maxdepth, currentdepth);
280 if (lowMem) {
281 fputs("Callstack (symbols only):\n", output);
282 backtrace_symbols_fd(callstackArray+offset, maxdepth, fd);
283 } else {
284 char **symbolStringList = backtrace_symbols(callstackArray, currentdepth);
285 if (symbolStringList) {
286 fputs("Callstack:\n", output);
287 char demangle_buffer[2048]= {0};
288 for (int i = offset; i < maxdepth; ++i) {
289 const char * const symbolString = symbolStringList[i];
290 char * realnameString = nullptr;
291 const char * const firstBracketName = strchr(symbolString, '(');
292 const char * const firstBracketAddress = strchr(symbolString, '[');
293 const char * const secondBracketAddress = strchr(firstBracketAddress, ']');
294 const char * const beginAddress = firstBracketAddress+3;
295 const int addressLen = int(secondBracketAddress-beginAddress);
296 const int padLen = int(ADDRESSDISPLAYLENGTH-addressLen);
297 if (demangling && firstBracketName) {
298 const char * const plus = strchr(firstBracketName, '+');
299 if (plus && (plus>(firstBracketName+1))) {
300 char input_buffer[1024]= {0};
301 strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1);
302 size_t length = getArrayLength(demangle_buffer);
303 int status=0;
304 // We're violating the specification - passing stack address instead of malloc'ed heap.
305 // Benefit is that no further heap is required, while there is sufficient stack...
306 realnameString = abi::__cxa_demangle(input_buffer, demangle_buffer, &length, &status); // non-NULL on success
307 }
308 }
309 const int ordinal=i-offset;
310 fprintf(output, "#%-2d 0x",
311 ordinal);
312 if (padLen>0)
313 fprintf(output, "%0*d",
314 padLen, 0);
315 if (realnameString) {
316 fprintf(output, "%.*s in %s\n",
317 (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3,
318 realnameString);
319 } else {
320 fprintf(output, "%.*s in %.*s\n",
321 (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3,
322 (int)(firstBracketAddress-symbolString), symbolString);
323 }
324 }
325 free(symbolStringList);
326 } else {
327 fputs("Callstack could not be obtained\n", output);
328 }
329 }
330 #undef ADDRESSDISPLAYLENGTH
331 #else
332 UNUSED(output);
333 UNUSED(demangling);
334 UNUSED(maxdepth);
335 UNUSED(lowMem);
336 #endif
337 }
338
339 #ifdef __USE_DYNAMIC_STACK_SIZE
340 static const size_t MYSTACKSIZE = 16*1024+32768; // wild guess about a reasonable buffer
341 #else
342 static const size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer
343 #endif
344 static char mytstack[MYSTACKSIZE]= {0}; // alternative stack for signal handler
345 static bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space. See CppCheckExecutor::check_wrapper()
346
347 /**
348 * \param[in] ptr address to be examined.
349 * \return true if address is supposed to be on stack (contrary to heap). If ptr is 0 false will be returned.
350 * If unknown better return false.
351 */
IsAddressOnStack(const void * ptr)352 static bool IsAddressOnStack(const void* ptr)
353 {
354 if (nullptr==ptr)
355 return false;
356 char a;
357 if (bStackBelowHeap)
358 return ptr < &a;
359 else
360 return ptr > &a;
361 }
362
363 /* (declare this list here, so it may be used in signal handlers in addition to main())
364 * A list of signals available in ISO C
365 * Check out http://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html
366 * For now we only want to detect abnormal behaviour for a few selected signals:
367 */
368
369 #define DECLARE_SIGNAL(x) std::make_pair(x, #x)
370 using Signalmap_t = std::map<int, std::string>;
371 static const Signalmap_t listofsignals = {
372 DECLARE_SIGNAL(SIGABRT),
373 DECLARE_SIGNAL(SIGBUS),
374 DECLARE_SIGNAL(SIGFPE),
375 DECLARE_SIGNAL(SIGILL),
376 DECLARE_SIGNAL(SIGINT),
377 DECLARE_SIGNAL(SIGQUIT),
378 DECLARE_SIGNAL(SIGSEGV),
379 DECLARE_SIGNAL(SIGSYS),
380 // don't care: SIGTERM
381 DECLARE_SIGNAL(SIGUSR1),
382 //DECLARE_SIGNAL(SIGUSR2) no usage currently
383 };
384 #undef DECLARE_SIGNAL
385 /*
386 * Entry pointer for signal handlers
387 * It uses functions which are not safe to be called from a signal handler,
388 * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04 has a whitelist)
389 * but when ending up here something went terribly wrong anyway.
390 * And all which is left is just printing some information and terminate.
391 */
CppcheckSignalHandler(int signo,siginfo_t * info,void * context)392 static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
393 {
394 int type = -1;
395 pid_t killid;
396 #if defined(__linux__) && defined(REG_ERR)
397 const ucontext_t* const uc = reinterpret_cast<const ucontext_t*>(context);
398 killid = (pid_t) syscall(SYS_gettid);
399 if (uc) {
400 type = (int)uc->uc_mcontext.gregs[REG_ERR] & 2;
401 }
402 #else
403 UNUSED(context);
404 killid = getpid();
405 #endif
406
407 const Signalmap_t::const_iterator it=listofsignals.find(signo);
408 const char * const signame = (it==listofsignals.end()) ? "unknown" : it->second.c_str();
409 bool printCallstack=true; // try to print a callstack?
410 bool lowMem=false; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then.
411 bool unexpectedSignal=true; // unexpected indicates program failure
412 bool terminate=true; // exit process/thread
413 const bool isAddressOnStack = IsAddressOnStack(info->si_addr);
414 FILE* output = CppCheckExecutor::getExceptionOutput();
415 switch (signo) {
416 case SIGABRT:
417 fputs("Internal error: cppcheck received signal ", output);
418 fputs(signame, output);
419 fputs(
420 #ifdef NDEBUG
421 " - out of memory?\n",
422 #else
423 " - out of memory or assertion?\n",
424 #endif
425 output);
426 lowMem=true; // educated guess
427 break;
428 case SIGBUS:
429 fputs("Internal error: cppcheck received signal ", output);
430 fputs(signame, output);
431 switch (info->si_code) {
432 case BUS_ADRALN: // invalid address alignment
433 fputs(" - BUS_ADRALN", output);
434 break;
435 case BUS_ADRERR: // nonexistent physical address
436 fputs(" - BUS_ADRERR", output);
437 break;
438 case BUS_OBJERR: // object-specific hardware error
439 fputs(" - BUS_OBJERR", output);
440 break;
441 #ifdef BUS_MCEERR_AR
442 case BUS_MCEERR_AR: // Hardware memory error consumed on a machine check;
443 fputs(" - BUS_MCEERR_AR", output);
444 break;
445 #endif
446 #ifdef BUS_MCEERR_AO
447 case BUS_MCEERR_AO: // Hardware memory error detected in process but not consumed
448 fputs(" - BUS_MCEERR_AO", output);
449 break;
450 #endif
451 default:
452 break;
453 }
454 fprintf(output, " (at 0x%lx).\n",
455 (unsigned long)info->si_addr);
456 break;
457 case SIGFPE:
458 fputs("Internal error: cppcheck received signal ", output);
459 fputs(signame, output);
460 switch (info->si_code) {
461 case FPE_INTDIV: // integer divide by zero
462 fputs(" - FPE_INTDIV", output);
463 break;
464 case FPE_INTOVF: // integer overflow
465 fputs(" - FPE_INTOVF", output);
466 break;
467 case FPE_FLTDIV: // floating-point divide by zero
468 fputs(" - FPE_FLTDIV", output);
469 break;
470 case FPE_FLTOVF: // floating-point overflow
471 fputs(" - FPE_FLTOVF", output);
472 break;
473 case FPE_FLTUND: // floating-point underflow
474 fputs(" - FPE_FLTUND", output);
475 break;
476 case FPE_FLTRES: // floating-point inexact result
477 fputs(" - FPE_FLTRES", output);
478 break;
479 case FPE_FLTINV: // floating-point invalid operation
480 fputs(" - FPE_FLTINV", output);
481 break;
482 case FPE_FLTSUB: // subscript out of range
483 fputs(" - FPE_FLTSUB", output);
484 break;
485 default:
486 break;
487 }
488 fprintf(output, " (at 0x%lx).\n",
489 (unsigned long)info->si_addr);
490 break;
491 case SIGILL:
492 fputs("Internal error: cppcheck received signal ", output);
493 fputs(signame, output);
494 switch (info->si_code) {
495 case ILL_ILLOPC: // illegal opcode
496 fputs(" - ILL_ILLOPC", output);
497 break;
498 case ILL_ILLOPN: // illegal operand
499 fputs(" - ILL_ILLOPN", output);
500 break;
501 case ILL_ILLADR: // illegal addressing mode
502 fputs(" - ILL_ILLADR", output);
503 break;
504 case ILL_ILLTRP: // illegal trap
505 fputs(" - ILL_ILLTRP", output);
506 break;
507 case ILL_PRVOPC: // privileged opcode
508 fputs(" - ILL_PRVOPC", output);
509 break;
510 case ILL_PRVREG: // privileged register
511 fputs(" - ILL_PRVREG", output);
512 break;
513 case ILL_COPROC: // coprocessor error
514 fputs(" - ILL_COPROC", output);
515 break;
516 case ILL_BADSTK: // internal stack error
517 fputs(" - ILL_BADSTK", output);
518 break;
519 default:
520 break;
521 }
522 fprintf(output, " (at 0x%lx).%s\n",
523 (unsigned long)info->si_addr,
524 (isAddressOnStack)?" Stackoverflow?":"");
525 break;
526 case SIGINT:
527 unexpectedSignal=false; // legal usage: interrupt application via CTRL-C
528 fputs("cppcheck received signal ", output);
529 fputs(signame, output);
530 printCallstack=true;
531 fputs(".\n", output);
532 break;
533 case SIGSEGV:
534 fputs("Internal error: cppcheck received signal ", output);
535 fputs(signame, output);
536 switch (info->si_code) {
537 case SEGV_MAPERR: // address not mapped to object
538 fputs(" - SEGV_MAPERR", output);
539 break;
540 case SEGV_ACCERR: // invalid permissions for mapped object
541 fputs(" - SEGV_ACCERR", output);
542 break;
543 default:
544 break;
545 }
546 fprintf(output, " (%sat 0x%lx).%s\n",
547 // cppcheck-suppress knownConditionTrueFalse ; FP
548 (type==-1)? "" :
549 (type==0) ? "reading " : "writing ",
550 (unsigned long)info->si_addr,
551 (isAddressOnStack)?" Stackoverflow?":""
552 );
553 break;
554 case SIGUSR1:
555 fputs("cppcheck received signal ", output);
556 fputs(signame, output);
557 fputs(".\n", output);
558 terminate=false;
559 break;
560 default:
561 fputs("Internal error: cppcheck received signal ", output);
562 fputs(signame, output);
563 fputs(".\n", output);
564 break;
565 }
566 if (printCallstack) {
567 print_stacktrace(output, true, -1, lowMem);
568 }
569 if (unexpectedSignal) {
570 fputs("\nPlease report this to the cppcheck developers!\n", output);
571 }
572 fflush(output);
573
574 if (terminate) {
575 // now let things proceed, shutdown and hopefully dump core for post-mortem analysis
576 struct sigaction act;
577 memset(&act, 0, sizeof(act));
578 act.sa_handler=SIG_DFL;
579 sigaction(signo, &act, nullptr);
580 kill(killid, signo);
581 }
582 }
583 #endif
584
585 #ifdef USE_WINDOWS_SEH
586 namespace {
587 const ULONG maxnamelength = 512;
588 struct IMAGEHLP_SYMBOL64_EXT : public IMAGEHLP_SYMBOL64 {
589 TCHAR nameExt[maxnamelength]; // actually no need to worry about character encoding here
590 };
591 typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
592 fpStackWalk64 pStackWalk64;
593 typedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64);
594 fpSymGetModuleBase64 pSymGetModuleBase64;
595 typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
596 fpSymGetSymFromAddr64 pSymGetSymFromAddr64;
597 typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
598 fpSymGetLineFromAddr64 pSymGetLineFromAddr64;
599 typedef DWORD (WINAPI *fpUnDecorateSymbolName)(const TCHAR*, PTSTR, DWORD, DWORD);
600 fpUnDecorateSymbolName pUnDecorateSymbolName;
601 typedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64);
602 fpSymFunctionTableAccess64 pSymFunctionTableAccess64;
603 typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL);
604 fpSymInitialize pSymInitialize;
605
606 HMODULE hLibDbgHelp;
607 // avoid explicit dependency on Dbghelp.dll
loadDbgHelp()608 bool loadDbgHelp()
609 {
610 hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll");
611 if (!hLibDbgHelp)
612 return false;
613 pStackWalk64 = (fpStackWalk64) ::GetProcAddress(hLibDbgHelp, "StackWalk64");
614 pSymGetModuleBase64 = (fpSymGetModuleBase64) ::GetProcAddress(hLibDbgHelp, "SymGetModuleBase64");
615 pSymGetSymFromAddr64 = (fpSymGetSymFromAddr64) ::GetProcAddress(hLibDbgHelp, "SymGetSymFromAddr64");
616 pSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)::GetProcAddress(hLibDbgHelp, "SymGetLineFromAddr64");
617 pSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)::GetProcAddress(hLibDbgHelp, "SymFunctionTableAccess64");
618 pSymInitialize = (fpSymInitialize) ::GetProcAddress(hLibDbgHelp, "SymInitialize");
619 pUnDecorateSymbolName = (fpUnDecorateSymbolName)::GetProcAddress(hLibDbgHelp, "UnDecorateSymbolName");
620 return true;
621 }
622
623
printCallstack(FILE * outputFile,PEXCEPTION_POINTERS ex)624 void printCallstack(FILE* outputFile, PEXCEPTION_POINTERS ex)
625 {
626 if (!loadDbgHelp())
627 return;
628 const HANDLE hProcess = GetCurrentProcess();
629 const HANDLE hThread = GetCurrentThread();
630 pSymInitialize(
631 hProcess,
632 nullptr,
633 TRUE
634 );
635 CONTEXT context = *(ex->ContextRecord);
636 STACKFRAME64 stack= {0};
637 #ifdef _M_IX86
638 stack.AddrPC.Offset = context.Eip;
639 stack.AddrPC.Mode = AddrModeFlat;
640 stack.AddrStack.Offset = context.Esp;
641 stack.AddrStack.Mode = AddrModeFlat;
642 stack.AddrFrame.Offset = context.Ebp;
643 stack.AddrFrame.Mode = AddrModeFlat;
644 #else
645 stack.AddrPC.Offset = context.Rip;
646 stack.AddrPC.Mode = AddrModeFlat;
647 stack.AddrStack.Offset = context.Rsp;
648 stack.AddrStack.Mode = AddrModeFlat;
649 stack.AddrFrame.Offset = context.Rsp;
650 stack.AddrFrame.Mode = AddrModeFlat;
651 #endif
652 IMAGEHLP_SYMBOL64_EXT symbol;
653 symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
654 symbol.MaxNameLength = maxnamelength;
655 DWORD64 displacement = 0;
656 int beyond_main=-1; // emergency exit, see below
657 for (ULONG frame = 0; ; frame++) {
658 BOOL result = pStackWalk64
659 (
660 #ifdef _M_IX86
661 IMAGE_FILE_MACHINE_I386,
662 #else
663 IMAGE_FILE_MACHINE_AMD64,
664 #endif
665 hProcess,
666 hThread,
667 &stack,
668 &context,
669 nullptr,
670 pSymFunctionTableAccess64,
671 pSymGetModuleBase64,
672 nullptr
673 );
674 if (!result) // official end...
675 break;
676 pSymGetSymFromAddr64(hProcess, (ULONG64)stack.AddrPC.Offset, &displacement, &symbol);
677 TCHAR undname[maxnamelength]= {0};
678 pUnDecorateSymbolName((const TCHAR*)symbol.Name, (PTSTR)undname, (DWORD)getArrayLength(undname), UNDNAME_COMPLETE);
679 if (beyond_main>=0)
680 ++beyond_main;
681 if (_tcscmp(undname, _T("main"))==0)
682 beyond_main=0;
683 fprintf(outputFile,
684 "%lu. 0x%08I64X in ",
685 frame, (ULONG64)stack.AddrPC.Offset);
686 fputs((const char *)undname, outputFile);
687 fputc('\n', outputFile);
688 if (0==stack.AddrReturn.Offset || beyond_main>2) // StackWalk64() sometimes doesn't reach any end...
689 break;
690 }
691
692 FreeLibrary(hLibDbgHelp);
693 hLibDbgHelp=nullptr;
694 }
695
writeMemoryErrorDetails(FILE * outputFile,PEXCEPTION_POINTERS ex,const char * description)696 void writeMemoryErrorDetails(FILE* outputFile, PEXCEPTION_POINTERS ex, const char* description)
697 {
698 fputs(description, outputFile);
699 fprintf(outputFile, " (instruction: 0x%p) ", ex->ExceptionRecord->ExceptionAddress);
700 // Using %p for ULONG_PTR later on, so it must have size identical to size of pointer
701 // This is not the universally portable solution but good enough for Win32/64
702 C_ASSERT(sizeof(void*) == sizeof(ex->ExceptionRecord->ExceptionInformation[1]));
703 switch (ex->ExceptionRecord->ExceptionInformation[0]) {
704 case 0:
705 fprintf(outputFile, "reading from 0x%p",
706 reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
707 break;
708 case 1:
709 fprintf(outputFile, "writing to 0x%p",
710 reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
711 break;
712 case 8:
713 fprintf(outputFile, "data execution prevention at 0x%p",
714 reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
715 break;
716 default:
717 break;
718 }
719 }
720
721 /*
722 * Any evaluation of the exception needs to be done here!
723 */
filterException(int code,PEXCEPTION_POINTERS ex)724 int filterException(int code, PEXCEPTION_POINTERS ex)
725 {
726 FILE *outputFile = stdout;
727 fputs("Internal error: ", outputFile);
728 switch (ex->ExceptionRecord->ExceptionCode) {
729 case EXCEPTION_ACCESS_VIOLATION:
730 writeMemoryErrorDetails(outputFile, ex, "Access violation");
731 break;
732 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
733 fputs("Out of array bounds", outputFile);
734 break;
735 case EXCEPTION_BREAKPOINT:
736 fputs("Breakpoint", outputFile);
737 break;
738 case EXCEPTION_DATATYPE_MISALIGNMENT:
739 fputs("Misaligned data", outputFile);
740 break;
741 case EXCEPTION_FLT_DENORMAL_OPERAND:
742 fputs("Denormalized floating-point value", outputFile);
743 break;
744 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
745 fputs("Floating-point divide-by-zero", outputFile);
746 break;
747 case EXCEPTION_FLT_INEXACT_RESULT:
748 fputs("Inexact floating-point value", outputFile);
749 break;
750 case EXCEPTION_FLT_INVALID_OPERATION:
751 fputs("Invalid floating-point operation", outputFile);
752 break;
753 case EXCEPTION_FLT_OVERFLOW:
754 fputs("Floating-point overflow", outputFile);
755 break;
756 case EXCEPTION_FLT_STACK_CHECK:
757 fputs("Floating-point stack overflow", outputFile);
758 break;
759 case EXCEPTION_FLT_UNDERFLOW:
760 fputs("Floating-point underflow", outputFile);
761 break;
762 case EXCEPTION_GUARD_PAGE:
763 fputs("Page-guard access", outputFile);
764 break;
765 case EXCEPTION_ILLEGAL_INSTRUCTION:
766 fputs("Illegal instruction", outputFile);
767 break;
768 case EXCEPTION_IN_PAGE_ERROR:
769 writeMemoryErrorDetails(outputFile, ex, "Invalid page access");
770 break;
771 case EXCEPTION_INT_DIVIDE_BY_ZERO:
772 fputs("Integer divide-by-zero", outputFile);
773 break;
774 case EXCEPTION_INT_OVERFLOW:
775 fputs("Integer overflow", outputFile);
776 break;
777 case EXCEPTION_INVALID_DISPOSITION:
778 fputs("Invalid exception dispatcher", outputFile);
779 break;
780 case EXCEPTION_INVALID_HANDLE:
781 fputs("Invalid handle", outputFile);
782 break;
783 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
784 fputs("Non-continuable exception", outputFile);
785 break;
786 case EXCEPTION_PRIV_INSTRUCTION:
787 fputs("Invalid instruction", outputFile);
788 break;
789 case EXCEPTION_SINGLE_STEP:
790 fputs("Single instruction step", outputFile);
791 break;
792 case EXCEPTION_STACK_OVERFLOW:
793 fputs("Stack overflow", outputFile);
794 break;
795 default:
796 fprintf(outputFile, "Unknown exception (%d)\n",
797 code);
798 break;
799 }
800 fputc('\n', outputFile);
801 printCallstack(outputFile, ex);
802 fflush(outputFile);
803 return EXCEPTION_EXECUTE_HANDLER;
804 }
805 }
806 #endif
807
808 /**
809 * Signal/SEH handling
810 * Has to be clean for using with SEH on windows, i.e. no construction of C++ object instances is allowed!
811 * TODO Check for multi-threading issues!
812 *
813 */
check_wrapper(CppCheck & cppcheck,int argc,const char * const argv[])814 int CppCheckExecutor::check_wrapper(CppCheck& cppcheck, int argc, const char* const argv[])
815 {
816 #ifdef USE_WINDOWS_SEH
817 FILE *outputFile = stdout;
818 __try {
819 return check_internal(cppcheck, argc, argv);
820 } __except (filterException(GetExceptionCode(), GetExceptionInformation())) {
821 // reporting to stdout may not be helpful within a GUI application...
822 fputs("Please report this to the cppcheck developers!\n", outputFile);
823 return -1;
824 }
825 #elif defined(USE_UNIX_SIGNAL_HANDLING)
826 // determine stack vs. heap
827 char stackVariable;
828 char *heapVariable=(char*)malloc(1);
829 bStackBelowHeap = &stackVariable < heapVariable;
830 free(heapVariable);
831
832 // set up alternative stack for signal handler
833 stack_t segv_stack;
834 segv_stack.ss_sp = mytstack;
835 segv_stack.ss_flags = 0;
836 segv_stack.ss_size = MYSTACKSIZE;
837 sigaltstack(&segv_stack, nullptr);
838
839 // install signal handler
840 struct sigaction act;
841 memset(&act, 0, sizeof(act));
842 act.sa_flags=SA_SIGINFO|SA_ONSTACK;
843 act.sa_sigaction=CppcheckSignalHandler;
844 for (std::map<int, std::string>::const_iterator sig=listofsignals.begin(); sig!=listofsignals.end(); ++sig) {
845 sigaction(sig->first, &act, nullptr);
846 }
847 return check_internal(cppcheck, argc, argv);
848 #else
849 return check_internal(cppcheck, argc, argv);
850 #endif
851 }
852
853 /*
854 * That is a method which gets called from check_wrapper
855 * */
check_internal(CppCheck & cppcheck,int,const char * const argv[])856 int CppCheckExecutor::check_internal(CppCheck& cppcheck, int /*argc*/, const char* const argv[])
857 {
858 Settings& settings = cppcheck.settings();
859 mSettings = &settings;
860 const bool std = tryLoadLibrary(settings.library, argv[0], "std.cfg");
861
862 for (const std::string &lib : settings.libraries) {
863 if (!tryLoadLibrary(settings.library, argv[0], lib.c_str())) {
864 const std::string msg("Failed to load the library " + lib);
865 const std::list<ErrorMessage::FileLocation> callstack;
866 ErrorMessage errmsg(callstack, emptyString, Severity::information, msg, "failedToLoadCfg", Certainty::normal);
867 reportErr(errmsg);
868 return EXIT_FAILURE;
869 }
870 }
871
872 bool posix = true;
873 if (settings.posix())
874 posix = tryLoadLibrary(settings.library, argv[0], "posix.cfg");
875 bool windows = true;
876 if (settings.isWindowsPlatform())
877 windows = tryLoadLibrary(settings.library, argv[0], "windows.cfg");
878
879 if (!std || !posix || !windows) {
880 const std::list<ErrorMessage::FileLocation> callstack;
881 const std::string msg("Failed to load " + std::string(!std ? "std.cfg" : !posix ? "posix.cfg" : "windows.cfg") + ". Your Cppcheck installation is broken, please re-install.");
882 #ifdef FILESDIR
883 const std::string details("The Cppcheck binary was compiled with FILESDIR set to \""
884 FILESDIR "\" and will therefore search for "
885 "std.cfg in " FILESDIR "/cfg.");
886 #else
887 const std::string cfgfolder(Path::fromNativeSeparators(Path::getPathFromFilename(argv[0])) + "cfg");
888 const std::string details("The Cppcheck binary was compiled without FILESDIR set. Either the "
889 "std.cfg should be available in " + cfgfolder + " or the FILESDIR "
890 "should be configured.");
891 #endif
892 ErrorMessage errmsg(callstack, emptyString, Severity::information, msg+" "+details, "failedToLoadCfg", Certainty::normal);
893 reportErr(errmsg);
894 return EXIT_FAILURE;
895 }
896
897 if (settings.reportProgress)
898 mLatestProgressOutputTime = std::time(nullptr);
899
900 if (!settings.outputFile.empty()) {
901 mErrorOutput = new std::ofstream(settings.outputFile);
902 }
903
904 if (settings.xml) {
905 reportErr(ErrorMessage::getXMLHeader());
906 }
907
908 if (!settings.buildDir.empty()) {
909 settings.loadSummaries();
910
911 std::list<std::string> fileNames;
912 for (std::map<std::string, std::size_t>::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i)
913 fileNames.emplace_back(i->first);
914 AnalyzerInformation::writeFilesTxt(settings.buildDir, fileNames, settings.userDefines, settings.project.fileSettings);
915 }
916
917 unsigned int returnValue = 0;
918 if (settings.jobs == 1) {
919 // Single process
920 settings.jointSuppressionReport = true;
921
922 std::size_t totalfilesize = 0;
923 for (std::map<std::string, std::size_t>::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) {
924 totalfilesize += i->second;
925 }
926
927 std::size_t processedsize = 0;
928 unsigned int c = 0;
929 if (settings.project.fileSettings.empty()) {
930 for (std::map<std::string, std::size_t>::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) {
931 if (!mSettings->library.markupFile(i->first)
932 || !mSettings->library.processMarkupAfterCode(i->first)) {
933 returnValue += cppcheck.check(i->first);
934 processedsize += i->second;
935 if (!settings.quiet)
936 reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize);
937 c++;
938 }
939 }
940 } else {
941 // filesettings
942 // check all files of the project
943 for (const ImportProject::FileSettings &fs : settings.project.fileSettings) {
944 returnValue += cppcheck.check(fs);
945 ++c;
946 if (!settings.quiet)
947 reportStatus(c, settings.project.fileSettings.size(), c, settings.project.fileSettings.size());
948 if (settings.clangTidy)
949 cppcheck.analyseClangTidy(fs);
950 }
951 }
952
953 // second loop to parse all markup files which may not work until all
954 // c/cpp files have been parsed and checked
955 for (std::map<std::string, std::size_t>::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) {
956 if (mSettings->library.markupFile(i->first) && mSettings->library.processMarkupAfterCode(i->first)) {
957 returnValue += cppcheck.check(i->first);
958 processedsize += i->second;
959 if (!settings.quiet)
960 reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize);
961 c++;
962 }
963 }
964 if (cppcheck.analyseWholeProgram())
965 returnValue++;
966 } else if (!ThreadExecutor::isEnabled()) {
967 std::cout << "No thread support yet implemented for this platform." << std::endl;
968 } else {
969 // Multiple processes
970 ThreadExecutor executor(mFiles, settings, *this);
971 returnValue = executor.check();
972 }
973
974 cppcheck.analyseWholeProgram(mSettings->buildDir, mFiles);
975
976 if (settings.severity.isEnabled(Severity::information) || settings.checkConfiguration) {
977 const bool enableUnusedFunctionCheck = cppcheck.isUnusedFunctionCheckEnabled();
978
979 if (settings.jointSuppressionReport) {
980 for (std::map<std::string, std::size_t>::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) {
981 const bool err = reportUnmatchedSuppressions(settings.nomsg.getUnmatchedLocalSuppressions(i->first, enableUnusedFunctionCheck));
982 if (err && returnValue == 0)
983 returnValue = settings.exitCode;
984 }
985 }
986
987 const bool err = reportUnmatchedSuppressions(settings.nomsg.getUnmatchedGlobalSuppressions(enableUnusedFunctionCheck));
988 if (err && returnValue == 0)
989 returnValue = settings.exitCode;
990 }
991
992 if (!settings.checkConfiguration) {
993 cppcheck.tooManyConfigsError("",0U);
994
995 if (settings.checks.isEnabled(Checks::missingInclude) && (Preprocessor::missingIncludeFlag || Preprocessor::missingSystemIncludeFlag)) {
996 const std::list<ErrorMessage::FileLocation> callStack;
997 ErrorMessage msg(callStack,
998 emptyString,
999 Severity::information,
1000 "Cppcheck cannot find all the include files (use --check-config for details)\n"
1001 "Cppcheck cannot find all the include files. Cppcheck can check the code without the "
1002 "include files found. But the results will probably be more accurate if all the include "
1003 "files are found. Please check your project's include directories and add all of them "
1004 "as include directories for Cppcheck. To see what files Cppcheck cannot find use "
1005 "--check-config.",
1006 Preprocessor::missingIncludeFlag ? "missingInclude" : "missingIncludeSystem",
1007 Certainty::normal);
1008 reportInfo(msg);
1009 }
1010 }
1011
1012 if (settings.xml) {
1013 reportErr(ErrorMessage::getXMLFooter());
1014 }
1015
1016 mSettings = nullptr;
1017 if (returnValue)
1018 return settings.exitCode;
1019 return 0;
1020 }
1021
1022 #ifdef _WIN32
1023 // fix trac ticket #439 'Cppcheck reports wrong filename for filenames containing 8-bit ASCII'
ansiToOEM(const std::string & msg,bool doConvert)1024 static inline std::string ansiToOEM(const std::string &msg, bool doConvert)
1025 {
1026 if (doConvert) {
1027 const unsigned msglength = msg.length();
1028 // convert ANSI strings to OEM strings in two steps
1029 std::vector<WCHAR> wcContainer(msglength);
1030 std::string result(msglength, '\0');
1031
1032 // ansi code page characters to wide characters
1033 MultiByteToWideChar(CP_ACP, 0, msg.data(), msglength, wcContainer.data(), msglength);
1034 // wide characters to oem codepage characters
1035 WideCharToMultiByte(CP_OEMCP, 0, wcContainer.data(), msglength, const_cast<char *>(result.data()), msglength, nullptr, nullptr);
1036
1037 return result; // hope for return value optimization
1038 }
1039 return msg;
1040 }
1041 #else
1042 // no performance regression on non-windows systems
1043 #define ansiToOEM(msg, doConvert) (msg)
1044 #endif
1045
reportErr(const std::string & errmsg)1046 void CppCheckExecutor::reportErr(const std::string &errmsg)
1047 {
1048 if (mErrorOutput)
1049 *mErrorOutput << errmsg << std::endl;
1050 else {
1051 std::cerr << ansiToOEM(errmsg, (mSettings == nullptr) ? true : !mSettings->xml) << std::endl;
1052 }
1053 }
1054
reportOut(const std::string & outmsg,Color c)1055 void CppCheckExecutor::reportOut(const std::string &outmsg, Color c)
1056 {
1057 std::cout << c << ansiToOEM(outmsg, true) << Color::Reset << std::endl;
1058 }
1059
reportProgress(const std::string & filename,const char stage[],const std::size_t value)1060 void CppCheckExecutor::reportProgress(const std::string &filename, const char stage[], const std::size_t value)
1061 {
1062 (void)filename;
1063
1064 if (!mLatestProgressOutputTime)
1065 return;
1066
1067 // Report progress messages every 10 seconds
1068 const std::time_t currentTime = std::time(nullptr);
1069 if (currentTime >= (mLatestProgressOutputTime + 10)) {
1070 mLatestProgressOutputTime = currentTime;
1071
1072 // format a progress message
1073 std::ostringstream ostr;
1074 ostr << "progress: "
1075 << stage
1076 << ' ' << value << '%';
1077
1078 // Report progress message
1079 reportOut(ostr.str());
1080 }
1081 }
1082
reportInfo(const ErrorMessage & msg)1083 void CppCheckExecutor::reportInfo(const ErrorMessage &msg)
1084 {
1085 reportErr(msg);
1086 }
1087
reportStatus(std::size_t fileindex,std::size_t filecount,std::size_t sizedone,std::size_t sizetotal)1088 void CppCheckExecutor::reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal)
1089 {
1090 if (filecount > 1) {
1091 std::ostringstream oss;
1092 const long percentDone = (sizetotal > 0) ? static_cast<long>(static_cast<long double>(sizedone) / sizetotal * 100) : 0;
1093 oss << fileindex << '/' << filecount
1094 << " files checked " << percentDone
1095 << "% done";
1096 std::cout << Color::FgBlue << oss.str() << Color::Reset << std::endl;
1097 }
1098 }
1099
reportErr(const ErrorMessage & msg)1100 void CppCheckExecutor::reportErr(const ErrorMessage &msg)
1101 {
1102 if (mShowAllErrors) {
1103 reportOut(msg.toXML());
1104 return;
1105 }
1106
1107 // Alert only about unique errors
1108 if (!mShownErrors.insert(msg.toString(mSettings->verbose)).second)
1109 return;
1110
1111 if (mSettings->xml)
1112 reportErr(msg.toXML());
1113 else
1114 reportErr(msg.toString(mSettings->verbose, mSettings->templateFormat, mSettings->templateLocation));
1115 }
1116
bughuntingReport(const std::string & str)1117 void CppCheckExecutor::bughuntingReport(const std::string &str)
1118 {
1119 if (!mSettings || str.empty())
1120 return;
1121 if (!mBugHuntingReport)
1122 mBugHuntingReport = new std::ofstream(mSettings->bugHuntingReport);
1123 (*mBugHuntingReport) << str << std::endl;
1124 }
1125
setExceptionOutput(FILE * exceptionOutput)1126 void CppCheckExecutor::setExceptionOutput(FILE* exceptionOutput)
1127 {
1128 mExceptionOutput = exceptionOutput;
1129 }
1130
getExceptionOutput()1131 FILE* CppCheckExecutor::getExceptionOutput()
1132 {
1133 return mExceptionOutput;
1134 }
1135
tryLoadLibrary(Library & destination,const char * basepath,const char * filename)1136 bool CppCheckExecutor::tryLoadLibrary(Library& destination, const char* basepath, const char* filename)
1137 {
1138 const Library::Error err = destination.load(basepath, filename);
1139
1140 if (err.errorcode == Library::ErrorCode::UNKNOWN_ELEMENT)
1141 std::cout << "cppcheck: Found unknown elements in configuration file '" << filename << "': " << err.reason << std::endl;
1142 else if (err.errorcode != Library::ErrorCode::OK) {
1143 std::cout << "cppcheck: Failed to load library configuration file '" << filename << "'. ";
1144 switch (err.errorcode) {
1145 case Library::ErrorCode::OK:
1146 break;
1147 case Library::ErrorCode::FILE_NOT_FOUND:
1148 std::cout << "File not found";
1149 break;
1150 case Library::ErrorCode::BAD_XML:
1151 std::cout << "Bad XML";
1152 break;
1153 case Library::ErrorCode::UNKNOWN_ELEMENT:
1154 std::cout << "Unexpected element";
1155 break;
1156 case Library::ErrorCode::MISSING_ATTRIBUTE:
1157 std::cout << "Missing attribute";
1158 break;
1159 case Library::ErrorCode::BAD_ATTRIBUTE_VALUE:
1160 std::cout << "Bad attribute value";
1161 break;
1162 case Library::ErrorCode::UNSUPPORTED_FORMAT:
1163 std::cout << "File is of unsupported format version";
1164 break;
1165 case Library::ErrorCode::DUPLICATE_PLATFORM_TYPE:
1166 std::cout << "Duplicate platform type";
1167 break;
1168 case Library::ErrorCode::PLATFORM_TYPE_REDEFINED:
1169 std::cout << "Platform type redefined";
1170 break;
1171 }
1172 if (!err.reason.empty())
1173 std::cout << " '" + err.reason + "'";
1174 std::cout << std::endl;
1175 return false;
1176 }
1177 return true;
1178 }
1179
1180 /**
1181 * Execute a shell command and read the output from it. Returns true if command terminated successfully.
1182 */
1183 // cppcheck-suppress passedByValue - "exe" copy needed in _WIN32 code
executeCommand(std::string exe,std::vector<std::string> args,const std::string & redirect,std::string * output_)1184 bool CppCheckExecutor::executeCommand(std::string exe, std::vector<std::string> args, const std::string &redirect, std::string *output_)
1185 {
1186 output_->clear();
1187
1188 std::string joinedArgs;
1189 for (const std::string &arg : args) {
1190 if (!joinedArgs.empty())
1191 joinedArgs += " ";
1192 if (arg.find(" ") != std::string::npos)
1193 joinedArgs += '"' + arg + '"';
1194 else
1195 joinedArgs += arg;
1196 }
1197
1198 #ifdef _WIN32
1199 // Extra quoutes are needed in windows if filename has space
1200 if (exe.find(" ") != std::string::npos)
1201 exe = "\"" + exe + "\"";
1202 const std::string cmd = exe + " " + joinedArgs + " " + redirect;
1203 std::unique_ptr<FILE, decltype(&_pclose)> pipe(_popen(cmd.c_str(), "r"), _pclose);
1204 #else
1205 const std::string cmd = exe + " " + joinedArgs + " " + redirect;
1206 std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
1207 #endif
1208 if (!pipe)
1209 return false;
1210 char buffer[1024];
1211 while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
1212 *output_ += buffer;
1213 return true;
1214 }
1215
1216