improved `Path` handling of mixed separators (#4808)

This commit is contained in:
Oliver Stöneberg 2023-02-23 22:34:05 +01:00 committed by GitHub
parent 346ecdb53a
commit 0797867758
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 107 additions and 33 deletions

View File

@ -32,6 +32,7 @@
#include <unistd.h> #include <unistd.h>
#else #else
#include <direct.h> #include <direct.h>
#include <stdexcept>
#endif #endif
#if defined(__CYGWIN__) #if defined(__CYGWIN__)
#include <strings.h> #include <strings.h>
@ -150,9 +151,12 @@ bool Path::isAbsolute(const std::string& path)
return false; return false;
} }
std::string Path::getRelativePath(const std::string& absolutePath, const std::vector<std::string>& basePaths) std::string Path::getRelativePath(std::string absolutePath, const std::vector<std::string>& basePaths)
{ {
for (const std::string &bp : basePaths) { absolutePath = Path::fromNativeSeparators(std::move(absolutePath));
for (std::string bp : basePaths) {
bp = Path::fromNativeSeparators(std::move(bp));
if (absolutePath == bp || bp.empty()) // Seems to be a file, or path is empty if (absolutePath == bp || bp.empty()) // Seems to be a file, or path is empty
continue; continue;
@ -161,7 +165,7 @@ std::string Path::getRelativePath(const std::string& absolutePath, const std::ve
if (endsWith(bp,'/')) if (endsWith(bp,'/'))
return absolutePath.substr(bp.length()); return absolutePath.substr(bp.length());
else if (absolutePath.size() > bp.size() && absolutePath[bp.length()] == '/') if (absolutePath.size() > bp.size() && absolutePath[bp.length()] == '/')
return absolutePath.substr(bp.length() + 1); return absolutePath.substr(bp.length() + 1);
} }
return absolutePath; return absolutePath;
@ -211,8 +215,12 @@ std::string Path::getAbsoluteFilePath(const std::string& filePath)
if (_fullpath(absolute, filePath.c_str(), _MAX_PATH)) if (_fullpath(absolute, filePath.c_str(), _MAX_PATH))
absolute_path = absolute; absolute_path = absolute;
#elif defined(__linux__) || defined(__sun) || defined(__hpux) || defined(__GNUC__) || defined(__CPPCHECK__) #elif defined(__linux__) || defined(__sun) || defined(__hpux) || defined(__GNUC__) || defined(__CPPCHECK__)
// realpath() only works with files that actually exist
char * absolute = realpath(filePath.c_str(), nullptr); char * absolute = realpath(filePath.c_str(), nullptr);
if (absolute) if (!absolute) {
const int err = errno;
throw std::runtime_error("realpath() failed with " + std::to_string(err));
}
absolute_path = absolute; absolute_path = absolute;
free(absolute); free(absolute);
#else #else
@ -221,15 +229,11 @@ std::string Path::getAbsoluteFilePath(const std::string& filePath)
return absolute_path; return absolute_path;
} }
std::string Path::stripDirectoryPart(const std::string &file) std::string Path::stripDirectoryPart(std::string file)
{ {
#if defined(_WIN32) && !defined(__MINGW32__) file = fromNativeSeparators(std::move(file));
const char native = '\\';
#else
const char native = '/';
#endif
const std::string::size_type p = file.rfind(native); const std::string::size_type p = file.rfind('/');
if (p != std::string::npos) { if (p != std::string::npos) {
return file.substr(p + 1); return file.substr(p + 1);
} }
@ -243,6 +247,8 @@ bool Path::fileExists(const std::string &file)
} }
std::string Path::join(std::string path1, std::string path2) { std::string Path::join(std::string path1, std::string path2) {
path1 = fromNativeSeparators(std::move(path1));
path2 = fromNativeSeparators(std::move(path2));
if (path1.empty() || path2.empty()) if (path1.empty() || path2.empty())
return path1 + path2; return path1 + path2;
if (path2.front() == '/') if (path2.front() == '/')

View File

@ -117,7 +117,7 @@ public:
* @param basePaths Paths to which it may be made relative. * @param basePaths Paths to which it may be made relative.
* @return relative path, if possible. Otherwise absolutePath is returned unchanged * @return relative path, if possible. Otherwise absolutePath is returned unchanged
*/ */
static std::string getRelativePath(const std::string& absolutePath, const std::vector<std::string>& basePaths); static std::string getRelativePath(std::string absolutePath, const std::vector<std::string>& basePaths);
/** /**
* @brief Get an absolute file path from a relative one. * @brief Get an absolute file path from a relative one.
@ -172,7 +172,7 @@ public:
* @param file filename to be stripped. path info is optional * @param file filename to be stripped. path info is optional
* @return filename without directory path part. * @return filename without directory path part.
*/ */
static std::string stripDirectoryPart(const std::string &file); static std::string stripDirectoryPart(std::string file);
/** /**
* @brief Checks if a File exists * @brief Checks if a File exists

View File

@ -178,6 +178,8 @@ bool cppcheck::Platform::platform(const std::string& platformstr, std::string& e
return false; return false;
} }
else { else {
if (verbose)
std::cout << "current working directory '" + Path::getCurrentPath() + "'" << std::endl;
bool found = false; bool found = false;
for (const std::string& path : paths) { for (const std::string& path : paths) {
if (verbose) if (verbose)
@ -203,19 +205,19 @@ bool cppcheck::Platform::loadPlatformFile(const char exename[], const std::strin
std::vector<std::string> filenames; std::vector<std::string> filenames;
filenames.push_back(filename); filenames.push_back(filename);
filenames.push_back(filename + ".xml"); filenames.push_back(filename + ".xml");
filenames.push_back("platforms/" + filename); filenames.push_back(Path::join("platforms", filename));
filenames.push_back("platforms/" + filename + ".xml"); filenames.push_back(Path::join("platforms", filename + ".xml"));
if (exename && (std::string::npos != Path::fromNativeSeparators(exename).find('/'))) { if (exename && (std::string::npos != Path::fromNativeSeparators(exename).find('/'))) {
filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + filename); filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + filename);
filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + "platforms/" + filename); filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + Path::join("platforms", filename));
filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + "platforms/" + filename + ".xml"); filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + Path::join("platforms", filename + ".xml"));
} }
#ifdef FILESDIR #ifdef FILESDIR
std::string filesdir = FILESDIR; std::string filesdir = FILESDIR;
if (!filesdir.empty() && filesdir[filesdir.size()-1] != '/') if (!filesdir.empty() && filesdir[filesdir.size()-1] != '/')
filesdir += '/'; filesdir += '/';
filenames.push_back(filesdir + ("platforms/" + filename)); filenames.push_back(filesdir + Path::join("platforms", filename));
filenames.push_back(filesdir + ("platforms/" + filename + ".xml")); filenames.push_back(filesdir + Path::join("platforms", filename + ".xml"));
#endif #endif
// open file.. // open file..

View File

@ -7474,7 +7474,7 @@ void ValueType::setDebugPath(const Token* tok, SourceLocation ctx, SourceLocatio
std::string file = ctx.file_name(); std::string file = ctx.file_name();
if (file.empty()) if (file.empty())
return; return;
std::string s = Path::stripDirectoryPart(file) + ":" + MathLib::toString(ctx.line()) + ": " + ctx.function_name() + std::string s = Path::stripDirectoryPart(std::move(file)) + ":" + MathLib::toString(ctx.line()) + ": " + ctx.function_name() +
" => " + local.function_name(); " => " + local.function_name();
debugPath.emplace_back(tok, std::move(s)); debugPath.emplace_back(tok, std::move(s));
} }

View File

@ -168,7 +168,7 @@ static void setSourceLocation(ValueFlow::Value& v,
std::string file = ctx.file_name(); std::string file = ctx.file_name();
if (file.empty()) if (file.empty())
return; return;
std::string s = Path::stripDirectoryPart(file) + ":" + MathLib::toString(ctx.line()) + ": " + ctx.function_name() + std::string s = Path::stripDirectoryPart(std::move(file)) + ":" + MathLib::toString(ctx.line()) + ": " + ctx.function_name() +
" => " + local.function_name() + ": " + debugString(v); " => " + local.function_name() + ": " + debugString(v);
v.debugPath.emplace_back(tok, std::move(s)); v.debugPath.emplace_back(tok, std::move(s));
} }

View File

@ -38,6 +38,8 @@ private:
TEST_CASE(is_cpp); TEST_CASE(is_cpp);
TEST_CASE(get_path_from_filename); TEST_CASE(get_path_from_filename);
TEST_CASE(join); TEST_CASE(join);
TEST_CASE(getAbsoluteFilePath);
TEST_CASE(stripDirectoryPart);
} }
void removeQuotationMarks() const { void removeQuotationMarks() const {
@ -93,6 +95,7 @@ private:
} }
void getRelative() const { void getRelative() const {
{
const std::vector<std::string> basePaths = { const std::vector<std::string> basePaths = {
"", // Don't crash with empty paths "", // Don't crash with empty paths
"C:/foo", "C:/foo",
@ -106,6 +109,35 @@ private:
ASSERT_EQUALS("C:/test.cpp", Path::getRelativePath("C:/test.cpp", 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)); ASSERT_EQUALS("C:/foobar/test.cpp", Path::getRelativePath("C:/foobar/test.cpp", basePaths));
} }
{
const std::vector<std::string> basePaths = {
"", // Don't crash with empty paths
"C:\\foo",
"C:\\bar\\",
"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));
}
{
const std::vector<std::string> basePaths = {
"", // Don't crash with empty paths
"/c/foo",
"/c/bar/",
"/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() const { void is_c() const {
ASSERT(Path::isC("index.cpp")==false); ASSERT(Path::isC("index.cpp")==false);
@ -141,6 +173,12 @@ private:
ASSERT_EQUALS("/tmp/", Path::getPathFromFilename("/tmp/index.h")); ASSERT_EQUALS("/tmp/", Path::getPathFromFilename("/tmp/index.h"));
ASSERT_EQUALS("a/b/c/", Path::getPathFromFilename("a/b/c/index.h")); ASSERT_EQUALS("a/b/c/", Path::getPathFromFilename("a/b/c/index.h"));
ASSERT_EQUALS("a/b/c/", Path::getPathFromFilename("a/b/c/")); ASSERT_EQUALS("a/b/c/", Path::getPathFromFilename("a/b/c/"));
ASSERT_EQUALS("S:\\tmp\\", Path::getPathFromFilename("S:\\tmp\\index.h"));
ASSERT_EQUALS("a\\b\\c\\", Path::getPathFromFilename("a\\b\\c\\index.h"));
ASSERT_EQUALS("a\\b\\c\\", Path::getPathFromFilename("a\\b\\c\\"));
ASSERT_EQUALS("S:\\a\\b\\c\\", Path::getPathFromFilename("S:\\a\\b\\c\\"));
ASSERT_EQUALS("S:/tmp/", Path::getPathFromFilename("S:/tmp/index.h"));
ASSERT_EQUALS("S:/a/b/c/", Path::getPathFromFilename("S:/a/b/c/index.h"));
} }
void join() const { void join() const {
@ -148,7 +186,35 @@ private:
ASSERT_EQUALS("a", Path::join("", "a")); ASSERT_EQUALS("a", Path::join("", "a"));
ASSERT_EQUALS("a/b", Path::join("a", "b")); ASSERT_EQUALS("a/b", Path::join("a", "b"));
ASSERT_EQUALS("a/b", Path::join("a/", "b")); ASSERT_EQUALS("a/b", Path::join("a/", "b"));
ASSERT_EQUALS("a/b", Path::join("a\\", "b"));
ASSERT_EQUALS("/b", Path::join("a", "/b")); ASSERT_EQUALS("/b", Path::join("a", "/b"));
ASSERT_EQUALS("/b", Path::join("a", "\\b"));
}
// TODO: this is quite messy - should Path::getAbsoluteFilePath() return normalized separators?
void getAbsoluteFilePath() const {
// Path::getAbsoluteFilePath() only works with existing paths on Linux
#ifdef _WIN32
const std::string cwd = Path::getCurrentPath();
ASSERT_EQUALS(Path::join(cwd, "a.h"), Path::fromNativeSeparators(Path::getAbsoluteFilePath("a.h")));
ASSERT_EQUALS(Path::join(cwd, "inc/a.h"), Path::fromNativeSeparators(Path::getAbsoluteFilePath("inc/a.h")));
const std::string cwd_down = Path::getPathFromFilename(cwd);
ASSERT_EQUALS(Path::join(cwd_down, "a.h"), Path::fromNativeSeparators(Path::getAbsoluteFilePath("../a.h")));
ASSERT_EQUALS(Path::join(cwd_down, "inc/a.h"), Path::fromNativeSeparators(Path::getAbsoluteFilePath("../inc/a.h")));
ASSERT_EQUALS(Path::join(cwd_down, "inc/a.h"), Path::fromNativeSeparators(Path::getAbsoluteFilePath("../inc/../inc/a.h")));
#endif
}
void stripDirectoryPart() const {
ASSERT_EQUALS("a.h", Path::stripDirectoryPart("a.h"));
ASSERT_EQUALS("a.h", Path::stripDirectoryPart("a/a.h"));
ASSERT_EQUALS("a.h", Path::stripDirectoryPart("a/b/a.h"));
ASSERT_EQUALS("a.h", Path::stripDirectoryPart("/mnt/a/b/a.h"));
ASSERT_EQUALS("a.h", Path::stripDirectoryPart("a\\a.h"));
ASSERT_EQUALS("a.h", Path::stripDirectoryPart("a\\b\\a.h"));
ASSERT_EQUALS("a.h", Path::stripDirectoryPart("S:\\a\\b\\a.h"));
ASSERT_EQUALS("a.h", Path::stripDirectoryPart("S:/a/b/a.h"));
} }
}; };