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