/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2023 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "cmdlineparser.h" #include "check.h" #include "config.h" #include "cppcheckexecutor.h" #include "errorlogger.h" #include "errortypes.h" #include "filelister.h" #include "importproject.h" #include "path.h" #include "platform.h" #include "settings.h" #include "standards.h" #include "suppressions.h" #include "timer.h" #include "utils.h" #include #include #include #include // EXIT_FAILURE #include #include // IWYU pragma: keep #include #include #include #include #include // IWYU pragma: keep #include #include #ifdef HAVE_RULES // xml is used for rules #include #endif static bool addFilesToList(const std::string& fileList, std::vector& pathNames) { std::istream *files; std::ifstream infile; if (fileList == "-") { // read from stdin files = &std::cin; } else { infile.open(fileList); if (!infile.is_open()) return false; files = &infile; } if (files && *files) { std::string fileName; // cppcheck-suppress accessMoved - FP while (std::getline(*files, fileName)) { // next line // cppcheck-suppress accessMoved - FP if (!fileName.empty()) { pathNames.emplace_back(std::move(fileName)); } } } return true; } static bool addIncludePathsToList(const std::string& fileList, std::list& pathNames) { std::ifstream files(fileList); if (files) { std::string pathName; // cppcheck-suppress accessMoved - FP while (std::getline(files, pathName)) { // next line if (!pathName.empty()) { pathName = Path::removeQuotationMarks(pathName); pathName = Path::fromNativeSeparators(pathName); // If path doesn't end with / or \, add it if (!endsWith(pathName, '/')) pathName += '/'; pathNames.emplace_back(std::move(pathName)); } } return true; } return false; } static bool addPathsToSet(const std::string& fileName, std::set& set) { std::list templist; if (!addIncludePathsToList(fileName, templist)) return false; set.insert(templist.cbegin(), templist.cend()); return true; } CmdLineParser::CmdLineParser(Settings &settings, Suppressions &suppressions, Suppressions &suppressionsNoFail) : mSettings(settings) , mSuppressions(suppressions) , mSuppressionsNoFail(suppressionsNoFail) {} void CmdLineParser::printMessage(const std::string &message) { std::cout << "cppcheck: " << message << std::endl; } void CmdLineParser::printError(const std::string &message) { printMessage("error: " + message); } #if defined(_WIN64) || defined(_WIN32) bool CmdLineParser::SHOW_DEF_PLATFORM_MSG = true; #endif // TODO: normalize/simplify/native all path parameters // TODO: error out on all missing given files/paths bool CmdLineParser::parseFromArgs(int argc, const char* const argv[]) { #if defined(_WIN64) || defined(_WIN32) bool default_platform = true; #endif bool def = false; bool maxconfigs = false; mSettings.exename = Path::getCurrentExecutablePath(argv[0]); for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { // User define if (std::strncmp(argv[i], "-D", 2) == 0) { std::string define; // "-D define" if (std::strcmp(argv[i], "-D") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-D' is missing."); return false; } define = argv[i]; } // "-Ddefine" else { define = 2 + argv[i]; } // No "=", append a "=1" if (define.find('=') == std::string::npos) define += "=1"; if (!mSettings.userDefines.empty()) mSettings.userDefines += ";"; mSettings.userDefines += define; def = true; } // -E else if (std::strcmp(argv[i], "-E") == 0) { mSettings.preprocessOnly = true; mSettings.quiet = true; } // Include paths else if (std::strncmp(argv[i], "-I", 2) == 0) { std::string path; // "-I path/" if (std::strcmp(argv[i], "-I") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-I' is missing."); return false; } path = argv[i]; } // "-Ipath/" else { path = 2 + argv[i]; } path = Path::removeQuotationMarks(path); path = Path::fromNativeSeparators(path); // If path doesn't end with / or \, add it if (!endsWith(path,'/')) path += '/'; mSettings.includePaths.emplace_back(std::move(path)); } // User undef else if (std::strncmp(argv[i], "-U", 2) == 0) { std::string undef; // "-U undef" if (std::strcmp(argv[i], "-U") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-U' is missing."); return false; } undef = argv[i]; } // "-Uundef" else { undef = 2 + argv[i]; } mSettings.userUndefs.insert(std::move(undef)); } else if (std::strncmp(argv[i], "--addon=", 8) == 0) mSettings.addons.emplace(argv[i]+8); else if (std::strncmp(argv[i],"--addon-python=", 15) == 0) mSettings.addonPython.assign(argv[i]+15); // Check configuration else if (std::strcmp(argv[i], "--check-config") == 0) mSettings.checkConfiguration = true; // Check code exhaustively else if (std::strcmp(argv[i], "--check-level=exhaustive") == 0) mSettings.setCheckLevelExhaustive(); // Check code with normal analysis else if (std::strcmp(argv[i], "--check-level=normal") == 0) mSettings.setCheckLevelNormal(); // Check library definitions else if (std::strcmp(argv[i], "--check-library") == 0) { mSettings.checkLibrary = true; } else if (std::strncmp(argv[i], "--checks-max-time=", 18) == 0) { if (!parseNumberArg(argv[i], 18, mSettings.checksMaxTime, true)) return false; } else if (std::strcmp(argv[i], "--clang") == 0) { mSettings.clang = true; } else if (std::strncmp(argv[i], "--clang=", 8) == 0) { mSettings.clang = true; mSettings.clangExecutable = argv[i] + 8; } else if (std::strncmp(argv[i], "--config-exclude=",17) ==0) { mSettings.configExcludePaths.insert(Path::fromNativeSeparators(argv[i] + 17)); } else if (std::strncmp(argv[i], "--config-excludes-file=", 23) == 0) { // open this file and read every input file (1 file name per line) const std::string cfgExcludesFile(23 + argv[i]); if (!addPathsToSet(cfgExcludesFile, mSettings.configExcludePaths)) { printError("unable to open config excludes file at '" + cfgExcludesFile + "'"); return false; } } else if (std::strncmp(argv[i], "--cppcheck-build-dir=", 21) == 0) { mSettings.buildDir = Path::fromNativeSeparators(argv[i] + 21); if (endsWith(mSettings.buildDir, '/')) mSettings.buildDir.pop_back(); if (!Path::directoryExists(mSettings.buildDir)) { printError("Directory '" + mSettings.buildDir + "' specified by --cppcheck-build-dir argument has to be existent."); return false; } } // Show --debug output after the first simplifications else if (std::strcmp(argv[i], "--debug") == 0 || std::strcmp(argv[i], "--debug-normal") == 0) mSettings.debugnormal = true; // Flag used for various purposes during debugging else if (std::strcmp(argv[i], "--debug-simplified") == 0) mSettings.debugSimplified = true; // Show template information else if (std::strcmp(argv[i], "--debug-template") == 0) mSettings.debugtemplate = true; // Show debug warnings else if (std::strcmp(argv[i], "--debug-warnings") == 0) mSettings.debugwarnings = true; else if (std::strncmp(argv[i], "--disable=", 10) == 0) { const std::string errmsg = mSettings.removeEnabled(argv[i] + 10); if (!errmsg.empty()) { printError(errmsg); return false; } } // documentation.. else if (std::strcmp(argv[i], "--doc") == 0) { std::ostringstream doc; // Get documentation.. for (const Check * it : Check::instances()) { const std::string& name(it->name()); const std::string info(it->classInfo()); if (!name.empty() && !info.empty()) doc << "## " << name << " ##\n" << info << "\n"; } std::cout << doc.str(); mExitAfterPrint = true; return true; } // dump cppcheck data else if (std::strcmp(argv[i], "--dump") == 0) mSettings.dump = true; else if (std::strncmp(argv[i], "--enable=", 9) == 0) { const std::string enable_arg = argv[i] + 9; const std::string errmsg = mSettings.addEnabled(enable_arg); if (!errmsg.empty()) { printError(errmsg); return false; } // when "style" is enabled, also enable "warning", "performance" and "portability" if (enable_arg.find("style") != std::string::npos) { mSettings.addEnabled("warning"); mSettings.addEnabled("performance"); mSettings.addEnabled("portability"); } if (enable_arg.find("information") != std::string::npos) { mSettings.addEnabled("missingInclude"); printMessage("'--enable=information' will no longer implicitly enable 'missingInclude' starting with 2.16. Please enable it explicitly if you require it."); } } // print all possible error messages.. else if (std::strcmp(argv[i], "--errorlist") == 0) { mShowErrorMessages = true; mSettings.xml = true; mExitAfterPrint = true; } // --error-exitcode=1 else if (std::strncmp(argv[i], "--error-exitcode=", 17) == 0) { if (!parseNumberArg(argv[i], 17, mSettings.exitCode)) return false; } // Exception handling inside cppcheck client else if (std::strcmp(argv[i], "--exception-handling") == 0) { mSettings.exceptionHandling = true; } // Exception handling inside cppcheck client else if (std::strncmp(argv[i], "--exception-handling=", 21) == 0) { const std::string exceptionOutfilename = argv[i] + 21; if (exceptionOutfilename != "stderr" && exceptionOutfilename != "stdout") { printError("invalid '--exception-handling' argument"); return false; } mSettings.exceptionHandling = true; CppCheckExecutor::setExceptionOutput((exceptionOutfilename == "stderr") ? stderr : stdout); } // Filter errors else if (std::strncmp(argv[i], "--exitcode-suppressions=", 24) == 0) { // exitcode-suppressions=filename.txt std::string filename = 24 + argv[i]; std::ifstream f(filename); if (!f.is_open()) { printError("couldn't open the file: \"" + filename + "\"."); return false; } const std::string errmsg(mSuppressionsNoFail.parseFile(f)); if (!errmsg.empty()) { printError(errmsg); return false; } } // use a file filter else if (std::strncmp(argv[i], "--file-filter=", 14) == 0) mSettings.fileFilters.emplace_back(argv[i] + 14); // file list specified else if (std::strncmp(argv[i], "--file-list=", 12) == 0) { // open this file and read every input file (1 file name per line) const std::string fileList = argv[i] + 12; if (!addFilesToList(fileList, mPathNames)) { printError("couldn't open the file: \"" + fileList + "\"."); return false; } } // Force checking of files that have "too many" configurations else if (std::strcmp(argv[i], "-f") == 0 || std::strcmp(argv[i], "--force") == 0) mSettings.force = true; // Print help else if (std::strcmp(argv[i], "-h") == 0 || std::strcmp(argv[i], "--help") == 0) { mPathNames.clear(); mShowHelp = true; mExitAfterPrint = true; break; } // Ignored paths else if (std::strncmp(argv[i], "-i", 2) == 0) { std::string path; // "-i path/" if (std::strcmp(argv[i], "-i") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-i' is missing."); return false; } path = argv[i]; } // "-ipath/" else { path = 2 + argv[i]; } if (!path.empty()) { path = Path::removeQuotationMarks(path); path = Path::fromNativeSeparators(path); path = Path::simplifyPath(path); if (FileLister::isDirectory(path)) { // If directory name doesn't end with / or \, add it if (!endsWith(path, '/')) path += '/'; } mIgnoredPaths.emplace_back(std::move(path)); } } else if (std::strncmp(argv[i], "--include=", 10) == 0) { mSettings.userIncludes.emplace_back(Path::fromNativeSeparators(argv[i] + 10)); } else if (std::strncmp(argv[i], "--includes-file=", 16) == 0) { // open this file and read every input file (1 file name per line) const std::string includesFile(16 + argv[i]); if (!addIncludePathsToList(includesFile, mSettings.includePaths)) { printError("unable to open includes file at '" + includesFile + "'"); return false; } } // Inconclusive checking else if (std::strcmp(argv[i], "--inconclusive") == 0) mSettings.certainty.enable(Certainty::inconclusive); // Enables inline suppressions. else if (std::strcmp(argv[i], "--inline-suppr") == 0) mSettings.inlineSuppressions = true; // Checking threads else if (std::strncmp(argv[i], "-j", 2) == 0) { std::string numberString; // "-j 3" if (std::strcmp(argv[i], "-j") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-j' is missing."); return false; } numberString = argv[i]; } // "-j3" else numberString = argv[i]+2; unsigned int tmp; std::string err; if (!strToInt(numberString, tmp, &err)) { printError("argument to '-j' is not valid - " + err + "."); return false; } if (tmp > 10000) { // This limit is here just to catch typos. If someone has // need for more jobs, this value should be increased. printError("argument for '-j' is allowed to be 10000 at max."); return false; } mSettings.jobs = tmp; } #ifdef THREADING_MODEL_FORK else if (std::strncmp(argv[i], "-l", 2) == 0) { std::string numberString; // "-l 3" if (std::strcmp(argv[i], "-l") == 0) { ++i; if (i >= argc || argv[i][0] == '-') { printError("argument to '-l' is missing."); return false; } numberString = argv[i]; } // "-l3" else numberString = argv[i]+2; int tmp; std::string err; if (!strToInt(numberString, tmp, &err)) { printError("argument to '-l' is not valid - " + err + "."); return false; } mSettings.loadAverage = tmp; } #endif // Enforce language (--language=, -x) else if (std::strncmp(argv[i], "--language=", 11) == 0 || std::strcmp(argv[i], "-x") == 0) { std::string str; if (argv[i][2]) { str = argv[i]+11; } else { i++; if (i >= argc || argv[i][0] == '-') { printError("no language given to '-x' option."); return false; } str = argv[i]; } if (str == "c") mSettings.enforcedLang = Settings::Language::C; else if (str == "c++") mSettings.enforcedLang = Settings::Language::CPP; else { printError("unknown language '" + str + "' enforced."); return false; } } // --library else if (std::strncmp(argv[i], "--library=", 10) == 0) { mSettings.libraries.emplace_back(argv[i] + 10); } // Set maximum number of #ifdef configurations to check else if (std::strncmp(argv[i], "--max-configs=", 14) == 0) { int tmp; if (!parseNumberArg(argv[i], 14, tmp)) return false; if (tmp < 1) { printError("argument to '--max-configs=' must be greater than 0."); return false; } mSettings.maxConfigs = tmp; mSettings.force = false; maxconfigs = true; } // max ctu depth else if (std::strncmp(argv[i], "--max-ctu-depth=", 16) == 0) { if (!parseNumberArg(argv[i], 16, mSettings.maxCtuDepth)) return false; } // Write results in file else if (std::strncmp(argv[i], "--output-file=", 14) == 0) mSettings.outputFile = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 14)); // Experimental: limit execution time for extended valueflow analysis. basic valueflow analysis // is always executed. else if (std::strncmp(argv[i], "--performance-valueflow-max-time=", 33) == 0) { if (!parseNumberArg(argv[i], 33, mSettings.performanceValueFlowMaxTime, true)) return false; } else if (std::strncmp(argv[i], "--performance-valueflow-max-if-count=", 37) == 0) { if (!parseNumberArg(argv[i], 37, mSettings.performanceValueFlowMaxIfCount, true)) return false; } // Specify platform else if (std::strncmp(argv[i], "--platform=", 11) == 0) { const std::string platform(11+argv[i]); std::string errstr; const std::vector paths = {argv[0]}; if (!mSettings.platform.set(platform, errstr, paths)) { printError(errstr); return false; } #if defined(_WIN64) || defined(_WIN32) default_platform = false; #endif // TODO: remove // these are loaded via external files and thus have Settings::PlatformFile set instead. // override the type so they behave like the regular platforms. if (platform == "unix32-unsigned") mSettings.platform.type = cppcheck::Platform::Type::Unix32; else if (platform == "unix64-unsigned") mSettings.platform.type = cppcheck::Platform::Type::Unix64; } // Write results in results.plist else if (std::strncmp(argv[i], "--plist-output=", 15) == 0) { mSettings.plistOutput = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 15)); if (mSettings.plistOutput.empty()) mSettings.plistOutput = "./"; else if (!endsWith(mSettings.plistOutput,'/')) mSettings.plistOutput += '/'; const std::string plistOutput = Path::toNativeSeparators(mSettings.plistOutput); if (!FileLister::isDirectory(plistOutput)) { std::string message("plist folder does not exist: \""); message += plistOutput; message += "\"."; printError(message); return false; } } // Special Cppcheck Premium options else if (std::strncmp(argv[i], "--premium=", 10) == 0 && isCppcheckPremium()) { if (!mSettings.premiumArgs.empty()) mSettings.premiumArgs += " "; const std::string p(argv[i] + 10); mSettings.premiumArgs += "--" + p; if (p == "misra-c-2012") mSettings.addons.emplace("misra"); } // --project else if (std::strncmp(argv[i], "--project=", 10) == 0) { mSettings.checkAllConfigurations = false; // Can be overridden with --max-configs or --force std::string projectFile = argv[i]+10; ImportProject::Type projType = mSettings.project.import(projectFile, &mSettings); mSettings.project.projectType = projType; if (projType == ImportProject::Type::CPPCHECK_GUI) { mPathNames = mSettings.project.guiProject.pathNames; for (const std::string &lib : mSettings.project.guiProject.libraries) mSettings.libraries.emplace_back(lib); const auto& excludedPaths = mSettings.project.guiProject.excludedPaths; std::copy(excludedPaths.cbegin(), excludedPaths.cend(), std::back_inserter(mIgnoredPaths)); std::string platform(mSettings.project.guiProject.platform); // keep existing platform from command-line intact if (!platform.empty()) { if (platform == "Unspecified") { printMessage("'Unspecified' is a deprecated platform type and will be removed in Cppcheck 2.14. Please use 'unspecified' instead."); platform = "unspecified"; } std::string errstr; const std::vector paths = {projectFile, argv[0]}; if (!mSettings.platform.set(platform, errstr, paths)) { printError(errstr); return false; } } if (!mSettings.project.guiProject.projectFile.empty()) { projectFile = mSettings.project.guiProject.projectFile; projType = mSettings.project.import(mSettings.project.guiProject.projectFile, &mSettings); } } if (projType == ImportProject::Type::VS_SLN || projType == ImportProject::Type::VS_VCXPROJ) { if (mSettings.project.guiProject.analyzeAllVsConfigs == "false") mSettings.project.selectOneVsConfig(mSettings.platform.type); if (!CppCheckExecutor::tryLoadLibrary(mSettings.library, argv[0], "windows.cfg")) { // This shouldn't happen normally. printError("failed to load 'windows.cfg'. Your Cppcheck installation is broken. Please re-install."); return false; } } if (projType == ImportProject::Type::MISSING) { printError("failed to open project '" + projectFile + "'. The file does not exist."); return false; } if (projType == ImportProject::Type::UNKNOWN) { printError("failed to load project '" + projectFile + "'. The format is unknown."); return false; } if (projType == ImportProject::Type::FAILURE) { printError("failed to load project '" + projectFile + "'. An error occurred."); return false; } } // --project-configuration else if (std::strncmp(argv[i], "--project-configuration=", 24) == 0) { mVSConfig = argv[i] + 24; if (!mVSConfig.empty() && (mSettings.project.projectType == ImportProject::Type::VS_SLN || mSettings.project.projectType == ImportProject::Type::VS_VCXPROJ)) mSettings.project.ignoreOtherConfigs(mVSConfig); } // Only print something when there are errors else if (std::strcmp(argv[i], "-q") == 0 || std::strcmp(argv[i], "--quiet") == 0) mSettings.quiet = true; // Output relative paths else if (std::strcmp(argv[i], "-rp") == 0 || std::strcmp(argv[i], "--relative-paths") == 0) mSettings.relativePaths = true; else if (std::strncmp(argv[i], "-rp=", 4) == 0 || std::strncmp(argv[i], "--relative-paths=", 17) == 0) { mSettings.relativePaths = true; if (argv[i][argv[i][3]=='='?4:17] != 0) { std::string paths = argv[i]+(argv[i][3]=='='?4:17); for (;;) { const std::string::size_type pos = paths.find(';'); if (pos == std::string::npos) { mSettings.basePaths.emplace_back(Path::fromNativeSeparators(paths)); break; } mSettings.basePaths.emplace_back(Path::fromNativeSeparators(paths.substr(0, pos))); paths.erase(0, pos + 1); } } else { printError("no paths specified for the '" + std::string(argv[i]) + "' option."); return false; } } // Report progress else if (std::strcmp(argv[i], "--report-progress") == 0) { mSettings.reportProgress = true; } #ifdef HAVE_RULES // Rule given at command line else if (std::strncmp(argv[i], "--rule=", 7) == 0) { Settings::Rule rule; rule.pattern = 7 + argv[i]; mSettings.rules.emplace_back(std::move(rule)); } // Rule file else if (std::strncmp(argv[i], "--rule-file=", 12) == 0) { tinyxml2::XMLDocument doc; if (doc.LoadFile(12+argv[i]) == tinyxml2::XML_SUCCESS) { tinyxml2::XMLElement *node = doc.FirstChildElement(); if (node && strcmp(node->Value(), "rules") == 0) node = node->FirstChildElement("rule"); for (; node && strcmp(node->Value(), "rule") == 0; node = node->NextSiblingElement()) { Settings::Rule rule; const tinyxml2::XMLElement *tokenlist = node->FirstChildElement("tokenlist"); if (tokenlist) rule.tokenlist = tokenlist->GetText(); const tinyxml2::XMLElement *pattern = node->FirstChildElement("pattern"); if (pattern) { rule.pattern = pattern->GetText(); } tinyxml2::XMLElement *message = node->FirstChildElement("message"); if (message) { const tinyxml2::XMLElement *severity = message->FirstChildElement("severity"); if (severity) rule.severity = Severity::fromString(severity->GetText()); const tinyxml2::XMLElement *id = message->FirstChildElement("id"); if (id) rule.id = id->GetText(); const tinyxml2::XMLElement *summary = message->FirstChildElement("summary"); if (summary) rule.summary = summary->GetText() ? summary->GetText() : ""; } if (!rule.pattern.empty()) mSettings.rules.emplace_back(std::move(rule)); } } else { printError("unable to load rule-file: " + std::string(12+argv[i])); return false; } } #endif // show timing information.. else if (std::strncmp(argv[i], "--showtime=", 11) == 0) { const std::string showtimeMode = argv[i] + 11; if (showtimeMode == "file") mSettings.showtime = SHOWTIME_MODES::SHOWTIME_FILE; else if (showtimeMode == "file-total") mSettings.showtime = SHOWTIME_MODES::SHOWTIME_FILE_TOTAL; else if (showtimeMode == "summary") mSettings.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY; else if (showtimeMode == "top5") mSettings.showtime = SHOWTIME_MODES::SHOWTIME_TOP5; else if (showtimeMode.empty()) mSettings.showtime = SHOWTIME_MODES::SHOWTIME_NONE; else { printError("unrecognized showtime mode: \"" + showtimeMode + "\". Supported modes: file, file-total, summary, top5."); return false; } } // --std else if (std::strncmp(argv[i], "--std=", 6) == 0) { const std::string std = argv[i] + 6; // TODO: print error when standard is unknown if (std::strncmp(std.c_str(), "c++", 3) == 0) { mSettings.standards.cpp = Standards::getCPP(std); } else if (std::strncmp(std.c_str(), "c", 1) == 0) { mSettings.standards.c = Standards::getC(std); } else { printError("unknown --std value '" + std + "'"); return false; } } else if (std::strncmp(argv[i], "--suppress=", 11) == 0) { const std::string suppression = argv[i]+11; const std::string errmsg(mSuppressions.addSuppressionLine(suppression)); if (!errmsg.empty()) { printError(errmsg); return false; } } // Filter errors else if (std::strncmp(argv[i], "--suppressions-list=", 20) == 0) { std::string filename = argv[i]+20; std::ifstream f(filename); if (!f.is_open()) { std::string message("couldn't open the file: \""); message += filename; message += "\"."; if (std::count(filename.cbegin(), filename.cend(), ',') > 0 || std::count(filename.cbegin(), filename.cend(), '.') > 1) { // If user tried to pass multiple files (we can only guess that) // e.g. like this: --suppressions-list=a.txt,b.txt // print more detailed error message to tell user how he can solve the problem message += "\nIf you want to pass two files, you can do it e.g. like this:"; message += "\n cppcheck --suppressions-list=a.txt --suppressions-list=b.txt file.cpp"; } printError(message); return false; } const std::string errmsg(mSuppressions.parseFile(f)); if (!errmsg.empty()) { printError(errmsg); return false; } } else if (std::strncmp(argv[i], "--suppress-xml=", 15) == 0) { const char * filename = argv[i] + 15; const std::string errmsg(mSuppressions.parseXmlFile(filename)); if (!errmsg.empty()) { printError(errmsg); return false; } } // TODO: deprecate "--template