From a71bb366823a7f2beb3fb16445233670feb8f1e7 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 19 May 2021 09:28:57 +0200 Subject: [PATCH] Utils: Centralize FilePath case sensitivity handling This could or possibly should the target device's file name case sensitivity into account by diverting to IDevice. However, as this is expensive and we are in time-critical path here, we go with "good enough" for now. The first approximation is "Anything unusual is not case sensitive" which is better than "Any device equals the host". Change-Id: Ib3d4a627abebd96a7285a855af66e0c800767767 Reviewed-by: Eike Ziller --- src/libs/utils/fileutils.cpp | 39 +++++++++++--------- src/libs/utils/fileutils.h | 2 + tests/auto/utils/fileutils/tst_fileutils.cpp | 35 ++++++++++++++++++ 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index a37a3f9b0bf..348386e550c 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -210,6 +210,18 @@ bool FilePath::isNewerThan(const QDateTime &timeStamp) const return false; } +Qt::CaseSensitivity FilePath::caseSensitivity() const +{ + if (m_scheme.isEmpty()) + return HostOsInfo::fileNameCaseSensitivity(); + + // FIXME: This could or possibly should the target device's file name case sensitivity + // into account by diverting to IDevice. However, as this is expensive and we are + // in time-critical path here, we go with "good enough" for now: + // The first approximation is "Anything unusual is not case sensitive" + return Qt::CaseSensitive; +} + /*! Recursively resolves symlinks if \a filePath is a symlink. To resolve symlinks anywhere in the path, see canonicalPath. @@ -994,12 +1006,9 @@ QDir FilePath::toDir() const bool FilePath::operator==(const FilePath &other) const { - if (m_scheme.isEmpty()) - return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) == 0; - - // FIXME: This should take the host's file name case sensitivity into account. - // The first approximation here is "Anything unusual is not case sensitive" - return m_data == other.m_data && m_host == other.m_host && m_scheme == other.m_scheme; + return QString::compare(m_data, other.m_data, caseSensitivity()) == 0 + && m_host == other.m_host + && m_scheme == other.m_scheme; } bool FilePath::operator!=(const FilePath &other) const @@ -1009,13 +1018,9 @@ bool FilePath::operator!=(const FilePath &other) const bool FilePath::operator<(const FilePath &other) const { - if (m_scheme.isEmpty()) - return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) < 0; - - // FIXME: This should take the host's file name case sensitivity into account. - // The first approximation here is "Anything unusual is not case sensitive" - if (m_data != other.m_data) - return m_data < other.m_data; + const int cmp = QString::compare(m_data, other.m_data, caseSensitivity()); + if (cmp != 0) + return cmp < 0; if (m_host != other.m_host) return m_host < other.m_host; return m_scheme < other.m_scheme; @@ -1046,7 +1051,7 @@ bool FilePath::isChildOf(const FilePath &s) const { if (s.isEmpty()) return false; - if (!m_data.startsWith(s.m_data, HostOsInfo::fileNameCaseSensitivity())) + if (!m_data.startsWith(s.m_data, caseSensitivity())) return false; if (m_data.size() <= s.m_data.size()) return false; @@ -1066,13 +1071,13 @@ bool FilePath::isChildOf(const QDir &dir) const /// \returns whether FilePath startsWith \a s bool FilePath::startsWith(const QString &s) const { - return m_data.startsWith(s, HostOsInfo::fileNameCaseSensitivity()); + return m_data.startsWith(s, caseSensitivity()); } /// \returns whether FilePath endsWith \a s bool FilePath::endsWith(const QString &s) const { - return m_data.endsWith(s, HostOsInfo::fileNameCaseSensitivity()); + return m_data.endsWith(s, caseSensitivity()); } bool FilePath::isDir() const @@ -1307,7 +1312,7 @@ void withNtfsPermissions(const std::function &task) std::hash::result_type std::hash::operator()(const std::hash::argument_type &fn) const { - if (Utils::HostOsInfo::fileNameCaseSensitivity() == Qt::CaseInsensitive) + if (fn.caseSensitivity() == Qt::CaseInsensitive) return hash()(fn.toString().toUpper().toStdString()); return hash()(fn.toString().toStdString()); } diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 8da4f64fdcb..67b40c4eff0 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -146,6 +146,8 @@ public: bool isDir() const; bool isNewerThan(const QDateTime &timeStamp) const; + Qt::CaseSensitivity caseSensitivity() const; + FilePath relativeChildPath(const FilePath &parent) const; FilePath relativePath(const FilePath &anchor) const; FilePath pathAppended(const QString &str) const; diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp index 258422305fd..381f359982c 100644 --- a/tests/auto/utils/fileutils/tst_fileutils.cpp +++ b/tests/auto/utils/fileutils/tst_fileutils.cpp @@ -52,6 +52,8 @@ private slots: void relativePath(); void fromToString_data(); void fromToString(); + void comparison_data(); + void comparison(); private: QTemporaryDir tempDir; @@ -329,5 +331,38 @@ void tst_fileutils::fromToString() QCOMPARE(copy.toString(), full); } +void tst_fileutils::comparison() +{ + QFETCH(QString, left); + QFETCH(QString, right); + QFETCH(bool, hostSensitive); + QFETCH(bool, expected); + + HostOsInfo::setOverrideFileNameCaseSensitivity( + hostSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + + FilePath l = FilePath::fromString(left); + FilePath r = FilePath::fromString(right); + QCOMPARE(l == r, expected); +} + +void tst_fileutils::comparison_data() +{ + QTest::addColumn("left"); + QTest::addColumn("right"); + QTest::addColumn("hostSensitive"); + QTest::addColumn("expected"); + + QTest::newRow("r1") << "Abc" << "abc" << true << false; + QTest::newRow("r2") << "Abc" << "abc" << false << true; + QTest::newRow("r3") << "x://y/Abc" << "x://y/abc" << true << false; + QTest::newRow("r4") << "x://y/Abc" << "x://y/abc" << false << false; + + QTest::newRow("s1") << "abc" << "abc" << true << true; + QTest::newRow("s2") << "abc" << "abc" << false << true; + QTest::newRow("s3") << "x://y/abc" << "x://y/abc" << true << true; + QTest::newRow("s4") << "x://y/abc" << "x://y/abc" << false << true; +} + QTEST_APPLESS_MAIN(tst_fileutils) #include "tst_fileutils.moc"