diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 3e4752377..202a9a4d4 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -249,6 +249,20 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[]) else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--force") == 0) _settings->_force = true; + // Output relative paths + else if (strcmp(argv[i], "-rp") == 0 || strcmp(argv[i], "--relative-paths") == 0) + _settings->_relativePaths = true; + else if (strncmp(argv[i], "-rp=", 4) == 0 || strncmp(argv[i], "--relative-paths=", 17) == 0) { + _settings->_relativePaths = true; + std::string paths = argv[i]+(argv[i][3]=='='?4:17); + std::string::size_type pos; + do { + pos = paths.find(';'); + _settings->_basePaths.push_back(Path::fromNativeSeparators(paths.substr(0, pos))); + paths.erase(0, pos+1); + } while (pos != std::string::npos); + } + // Write results in results.xml else if (strcmp(argv[i], "--xml") == 0) _settings->_xml = true; @@ -684,6 +698,10 @@ bool CmdLineParser::ParseFromArgs(int argc, const char* const argv[]) return false; } + // Use paths _pathnames if no base paths for relative path output are given + if (_settings->_basePaths.empty() && _settings->_relativePaths) + _settings->_basePaths = _pathnames; + return true; } @@ -780,6 +798,11 @@ void CmdLineParser::PrintHelp() const " * win64\n" " 64 bit Windows\n" " -q, --quiet Only print error messages.\n" + " -rp, --relative-paths\n" + " -rp=, --relative-paths=\n" + " Use relative paths in output. When given, are\n" + " used as base. You can separate multiple paths by ';'.\n" + " Otherwise path where source files are searched is used.\n" " --report-progress Report progress messages while checking a file.\n" #ifdef HAVE_RULES " --rule= Match regular expression.\n" diff --git a/lib/path.cpp b/lib/path.cpp index bbd1c83bb..20122fd5a 100644 --- a/lib/path.cpp +++ b/lib/path.cpp @@ -154,6 +154,21 @@ std::string Path::getFilenameExtensionInLowerCase(const std::string &path) return extension; } +std::string Path::getRelativePath(const std::string& absolutePath, const std::vector& basePaths) +{ + for (std::vector::const_iterator i = basePaths.begin(); i != basePaths.end(); ++i) { + if (absolutePath == *i) // Seems to be a file + continue; + + bool endsWithSep = (*i)[i->length()-1] == '/'; + if (absolutePath.compare(0, i->length(), *i) == 0 && absolutePath[i->length() - (endsWithSep?1:0)] == '/') { + std::string rest = absolutePath.substr(i->length() + (endsWithSep?0:1)); + return rest; + } + } + return absolutePath; +} + bool Path::isC(const std::string &path) { // In unix, ".C" is concidered C++ file diff --git a/lib/path.h b/lib/path.h index 5ecd71d40..8b319a4ec 100644 --- a/lib/path.h +++ b/lib/path.h @@ -20,6 +20,7 @@ #define PATH_H_INCLUDED #include +#include /// @addtogroup Core /// @{ @@ -84,6 +85,14 @@ public: */ static std::string getFilenameExtensionInLowerCase(const std::string &path); + /** + * @brief Create a relative path from an absolute one, if absolute path is inside the basePaths. + * @param absolutePath Path to be made relative. + * @param basePaths Paths to which it may be made relative. + * @return relative path, if possible. Otherwise absolutePath is returned unchanged + */ + static std::string getRelativePath(const std::string& absolutePath, const std::vector& basePaths); + /** * @brief Check if the file extension indicates that it's a C/C++ source file. * Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index 5136efbce..05fc23089 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -2002,7 +2002,7 @@ void Preprocessor::handleIncludes(std::string &code, const std::string &filePath if (!processedFile.empty()) { // Remove space characters that are after or before new line character - processedFile = "#file \"" + filename + "\"\n" + processedFile + "\n#endfile"; + processedFile = "#file \"" + Path::fromNativeSeparators(filename) + "\"\n" + processedFile + "\n#endfile"; code.insert(pos, processedFile); path = filename; diff --git a/lib/settings.h b/lib/settings.h index 0eb5dfb91..b9d6ab076 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -20,6 +20,7 @@ #define settingsH #include +#include #include #include #include "suppressions.h" @@ -90,6 +91,12 @@ public: /** @brief Force checking the files with "too many" configurations (--force). */ bool _force; + /** @brief Use relative paths in output. */ + bool _relativePaths; + + /** @brief Paths used as base for conversion to relative paths. */ + std::vector _basePaths; + /** @brief write XML results (--xml) */ bool _xml; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index cf1c8577f..485d3ff77 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -345,7 +345,7 @@ void Tokenizer::createTokens(std::istream &code) if (!foundOurfile) { // The "_files" vector remembers what files have been tokenized.. - _files.push_back(Path::simplifyPath(line.c_str())); + _files.push_back(Path::getRelativePath(Path::simplifyPath(line.c_str()), _settings->_basePaths)); FileIndex = static_cast(_files.size() - 1); } @@ -1943,7 +1943,7 @@ bool Tokenizer::tokenize(std::istream &code, _configuration = configuration; // The "_files" vector remembers what files have been tokenized.. - _files.push_back(Path::simplifyPath(FileName)); + _files.push_back(Path::getRelativePath(Path::simplifyPath(FileName), _settings->_basePaths)); createTokens(code); diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index dfbfdd892..b35dc1b8c 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -42,6 +42,7 @@ private: TEST_CASE(debugwarnings); TEST_CASE(forceshort); TEST_CASE(forcelong); + TEST_CASE(relativePaths); TEST_CASE(quietshort); TEST_CASE(quietlong); TEST_CASE(defines_noarg); @@ -248,6 +249,42 @@ private: ASSERT_EQUALS(true, settings._force); } + void relativePaths() { + REDIRECT; + Settings settings; + CmdLineParser parser(&settings); + + const char *argvs[] = {"cppcheck", "-rp", "file.cpp"}; + ASSERT(parser.ParseFromArgs(3, argvs)); + ASSERT_EQUALS(true, settings._relativePaths); + + settings._relativePaths = false; + + const char *argvl[] = {"cppcheck", "--relative-paths", "file.cpp"}; + ASSERT(parser.ParseFromArgs(3, argvl)); + ASSERT_EQUALS(true, settings._relativePaths); + + settings._relativePaths = false; + settings._basePaths.clear(); + + const char *argvsp[] = {"cppcheck", "-rp=C:/foo;C:\\bar", "file.cpp"}; + ASSERT(parser.ParseFromArgs(3, argvsp)); + ASSERT_EQUALS(true, settings._relativePaths); + ASSERT_EQUALS(2, settings._basePaths.size()); + ASSERT_EQUALS("C:/foo", settings._basePaths[0]); + ASSERT_EQUALS("C:/bar", settings._basePaths[1]); + + settings._relativePaths = false; + settings._basePaths.clear(); + + const char *argvlp[] = {"cppcheck", "--relative-paths=C:/foo;C:\\bar", "file.cpp"}; + ASSERT(parser.ParseFromArgs(3, argvlp)); + ASSERT_EQUALS(true, settings._relativePaths); + ASSERT_EQUALS(2, settings._basePaths.size()); + ASSERT_EQUALS("C:/foo", settings._basePaths[0]); + ASSERT_EQUALS("C:/bar", settings._basePaths[1]); + } + void quietshort() { REDIRECT; const char *argv[] = {"cppcheck", "-q", "file.cpp"}; diff --git a/test/testpath.cpp b/test/testpath.cpp index e376d1f23..14b18cd3d 100644 --- a/test/testpath.cpp +++ b/test/testpath.cpp @@ -30,6 +30,7 @@ private: void run() { TEST_CASE(simplify_path); TEST_CASE(accept_file); + TEST_CASE(getRelative); TEST_CASE(is_c); TEST_CASE(is_cpp); TEST_CASE(is_java); @@ -76,6 +77,19 @@ private: ASSERT(Path::acceptFile("C")==false); } + void getRelative() { + std::vector basePaths; + basePaths.push_back("C:/foo"); + basePaths.push_back("C:/bar/"); + basePaths.push_back("C:/test.cpp"); + + ASSERT_EQUALS("x.c", Path::getRelativePath("C:/foo/x.c", basePaths)); + ASSERT_EQUALS("y.c", Path::getRelativePath("C:/bar/y.c", basePaths)); + ASSERT_EQUALS("foo/y.c", Path::getRelativePath("C:/bar/foo/y.c", basePaths)); + ASSERT_EQUALS("C:/test.cpp", Path::getRelativePath("C:/test.cpp", basePaths)); + ASSERT_EQUALS("C:/foobar/test.cpp", Path::getRelativePath("C:/foobar/test.cpp", basePaths)); + } + void is_c() { ASSERT(Path::isC("index.cpp")==false); ASSERT(Path::isC("")==false);