diff options
author | Edward Welbourne <edward.welbourne@qt.io> | 2018-05-22 16:50:38 +0200 |
---|---|---|
committer | Edward Welbourne <edward.welbourne@qt.io> | 2018-07-23 14:28:38 +0000 |
commit | 27f1f84c1c220c5dadc739d5b056ec0f1b9f63a6 (patch) | |
tree | 643a49c5b1e14924a48cb6b229c7b6cd6d723de0 | |
parent | b616a8a8cda9a5e8ee9055f4835fd0d080bb6933 (diff) |
Let QDir::absoluteFilePath() use isAbsolutePath() for resource paths
Using QFileSystemEntry::isAbsolute() broke handling of resource paths.
Extended QDir::absoluteFilePath() tests to cover absolute resource path
and some UNC variants also resolved in the same fix.
Amend existing filePath tests to use drives where needed.
Task-number: QTBUG-68337
Change-Id: I4f02cf67828ad93e562857118f8442037f18bab7
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r-- | src/corelib/io/qdir.cpp | 96 | ||||
-rw-r--r-- | tests/auto/corelib/io/qdir/tst_qdir.cpp | 44 |
2 files changed, 89 insertions, 51 deletions
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index e4d384e4ca..10aee73a2f 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -715,6 +715,37 @@ QString QDir::dirName() const return d->dirEntry.fileName(); } + +#ifdef Q_OS_WIN +static int drivePrefixLength(const QString &path) +{ + // Used to extract path's drive for use as prefix for an "absolute except for drive" path + const int size = path.length(); + int drive = 2; // length of drive prefix + if (size > 1 && path.at(1).unicode() == ':') { + if (Q_UNLIKELY(!path.at(0).isLetter())) + return 0; + } else if (path.startsWith(QLatin1String("//"))) { + // UNC path; use its //server/share part as "drive" - it's as sane a + // thing as we can do. + for (int i = 2; i-- > 0; ) { // Scan two "path fragments": + while (drive < size && path.at(drive).unicode() == '/') + drive++; + if (drive >= size) { + qWarning("Base directory starts with neither a drive nor a UNC share: %s", + qUtf8Printable(QDir::toNativeSeparators(path))); + return 0; + } + while (drive < size && path.at(drive).unicode() != '/') + drive++; + } + } else { + return 0; + } + return drive; +} +#endif // Q_OS_WIN + /*! Returns the path name of a file in the directory. Does \e not check if the file actually exists in the directory; but see @@ -727,16 +758,27 @@ QString QDir::dirName() const QString QDir::filePath(const QString &fileName) const { const QDirPrivate* d = d_ptr.constData(); - if (isAbsolutePath(fileName)) + // Mistrust our own isAbsolutePath() for real files; Q_OS_WIN needs a drive. + if (fileName.startsWith(QLatin1Char(':')) // i.e. resource path + ? isAbsolutePath(fileName) : QFileSystemEntry(fileName).isAbsolute()) { return fileName; + } QString ret = d->dirEntry.filePath(); - if (!fileName.isEmpty()) { - if (!ret.isEmpty() && ret[(int)ret.length()-1] != QLatin1Char('/') && fileName[0] != QLatin1Char('/')) - ret += QLatin1Char('/'); - ret += fileName; + if (fileName.isEmpty()) + return ret; + +#ifdef Q_OS_WIN + if (fileName.startsWith(QLatin1Char('/')) || fileName.startsWith(QLatin1Char('\\'))) { + // Handle the "absolute except for drive" case (i.e. \blah not c:\blah): + const int drive = drivePrefixLength(ret); + return drive > 0 ? ret.leftRef(drive) % fileName : fileName; } - return ret; +#endif // Q_OS_WIN + + if (ret.isEmpty() || ret.endsWith(QLatin1Char('/'))) + return ret % fileName; + return ret % QLatin1Char('/') % fileName; } /*! @@ -750,9 +792,11 @@ QString QDir::filePath(const QString &fileName) const QString QDir::absoluteFilePath(const QString &fileName) const { const QDirPrivate* d = d_ptr.constData(); - // Don't trust our own isAbsolutePath(); Q_OS_WIN needs a drive. - if (QFileSystemEntry(fileName).isAbsolute()) + // Mistrust our own isAbsolutePath() for real files; Q_OS_WIN needs a drive. + if (fileName.startsWith(QLatin1Char(':')) // i.e. resource path + ? isAbsolutePath(fileName) : QFileSystemEntry(fileName).isAbsolute()) { return fileName; + } d->resolveAbsoluteEntry(); const QString absoluteDirPath = d->absoluteDirEntry.filePath(); @@ -760,35 +804,15 @@ QString QDir::absoluteFilePath(const QString &fileName) const return absoluteDirPath; #ifdef Q_OS_WIN // Handle the "absolute except for drive" case (i.e. \blah not c:\blah): - int size = absoluteDirPath.length(); - if ((fileName.startsWith(QLatin1Char('/')) - || fileName.startsWith(QLatin1Char('\\'))) - && size > 1) { + if (fileName.startsWith(QLatin1Char('/')) || fileName.startsWith(QLatin1Char('\\'))) { // Combine absoluteDirPath's drive with fileName - int drive = 2; // length of drive prefix - if (Q_UNLIKELY(absoluteDirPath.at(1).unicode() != ':')) { - // Presumably, absoluteDirPath is an UNC path; use its //server/share - // part as "drive" - it's as sane a thing as we can do. - for (int i = 2; i-- > 0; ) { // Scan two "path fragments": - while (drive < size && absoluteDirPath.at(drive).unicode() == '/') - drive++; - if (drive >= size) { - qWarning("Base directory starts with neither a drive nor a UNC share: %s", - qPrintable(QDir::toNativeSeparators(absoluteDirPath))); - return QString(); - } - while (drive < size && absoluteDirPath.at(drive).unicode() != '/') - drive++; - } - // We'll append fileName, which starts with a slash; so omit trailing slash: - if (absoluteDirPath.at(drive).unicode() == '/') - drive--; - } else if (!absoluteDirPath.at(0).isLetter()) { - qWarning("Base directory's drive is not a letter: %s", - qPrintable(QDir::toNativeSeparators(absoluteDirPath))); - return QString(); - } - return absoluteDirPath.leftRef(drive) % fileName; + const int drive = drivePrefixLength(absoluteDirPath); + if (Q_LIKELY(drive)) + return absoluteDirPath.leftRef(drive) % fileName; + + qWarning("Base directory's drive is not a letter: %s", + qUtf8Printable(QDir::toNativeSeparators(absoluteDirPath))); + return QString(); } #endif // Q_OS_WIN if (!absoluteDirPath.endsWith(QLatin1Char('/'))) diff --git a/tests/auto/corelib/io/qdir/tst_qdir.cpp b/tests/auto/corelib/io/qdir/tst_qdir.cpp index 83492188a9..afa15fe895 100644 --- a/tests/auto/corelib/io/qdir/tst_qdir.cpp +++ b/tests/auto/corelib/io/qdir/tst_qdir.cpp @@ -56,6 +56,12 @@ #define Q_NO_SYMLINKS #endif +#ifdef Q_OS_WIN +#define DRIVE "Q:" +#else +#define DRIVE +#endif + #ifdef QT_BUILD_INTERNAL QT_BEGIN_NAMESPACE @@ -1385,14 +1391,12 @@ void tst_QDir::absoluteFilePath_data() QTest::addColumn<QString>("expectedFilePath"); #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - QTest::newRow("UNC") << "//machine" << "share" << "//machine/share"; - QTest::newRow("Drive") << "c:/side/town" << "/my/way/home" << "c:/my/way/home"; -#endif - -#ifdef Q_OS_WIN -#define DRIVE "Q:" -#else -#define DRIVE + QTest::newRow("UNC-rel") << "//machine/share" << "dir" << "//machine/share/dir"; + QTest::newRow("UNC-abs") << "//machine/share/path/to/blah" << "/dir" << "//machine/share/dir"; + QTest::newRow("UNC-UNC") << "//machine/share/path/to/blah" << "//host/share/path" << "//host/share/path"; + QTest::newRow("Drive-UNC") << "c:/side/town" << "//host/share/path" << "//host/share/path"; + QTest::newRow("Drive-LTUNC") << "c:/side/town" << "\\/leaning\\toothpick/path" << "\\/leaning\\toothpick/path"; + QTest::newRow("Drive-abs") << "c:/side/town" << "/my/way/home" << "c:/my/way/home"; #endif QTest::newRow("0") << DRIVE "/etc" << "/passwd" << DRIVE "/passwd"; @@ -1401,8 +1405,10 @@ void tst_QDir::absoluteFilePath_data() QTest::newRow("3") << "relative" << "path" << QDir::currentPath() + "/relative/path"; QTest::newRow("4") << "" << "" << QDir::currentPath(); - QTest::newRow("resource") << ":/prefix" << "foo.bar" << ":/prefix/foo.bar"; -#undef DRIVE + // Resource paths are absolute: + QTest::newRow("resource-rel") << ":/prefix" << "foo.bar" << ":/prefix/foo.bar"; + QTest::newRow("abs-res-res") << ":/prefix" << ":/abc.txt" << ":/abc.txt"; + QTest::newRow("abs-res-path") << DRIVE "/etc" << ":/abc.txt" << ":/abc.txt"; } void tst_QDir::absoluteFilePath() @@ -1517,12 +1523,17 @@ void tst_QDir::filePath_data() QTest::addColumn<QString>("fileName"); QTest::addColumn<QString>("expectedFilePath"); - QTest::newRow("0") << "/etc" << "/passwd" << "/passwd"; - QTest::newRow("1") << "/etc" << "passwd" << "/etc/passwd"; - QTest::newRow("2") << "/" << "passwd" << "/passwd"; - QTest::newRow("3") << "relative" << "path" << "relative/path"; - QTest::newRow("4") << "" << "" << "."; + QTest::newRow("abs-abs") << DRIVE "/etc" << DRIVE "/passwd" << DRIVE "/passwd"; + QTest::newRow("abs-rel") << DRIVE "/etc" << "passwd" << DRIVE "/etc/passwd"; + QTest::newRow("root-rel") << DRIVE "/" << "passwd" << DRIVE "/passwd"; + QTest::newRow("rel-rel") << "relative" << "path" << "relative/path"; + QTest::newRow("empty-empty") << "" << "" << "."; QTest::newRow("resource") << ":/prefix" << "foo.bar" << ":/prefix/foo.bar"; +#ifdef Q_OS_WIN + QTest::newRow("abs-LTUNC") << "Q:/path" << "\\/leaning\\tooth/pick" << "\\/leaning\\tooth/pick"; + QTest::newRow("LTUNC-slash") << "\\/leaning\\tooth/pick" << "/path" << "//leaning/tooth/path"; + QTest::newRow("LTUNC-abs") << "\\/leaning\\tooth/pick" << "Q:/path" << "Q:/path"; +#endif } void tst_QDir::filePath() @@ -1588,6 +1599,9 @@ void tst_QDir::exists2_data() QTest::newRow("2") << "" << false; QTest::newRow("3") << "testData" << true; QTest::newRow("4") << "/testData" << false; +#ifdef Q_OS_WIN + QTest::newRow("abs") << "Q:/testData" << false; +#endif QTest::newRow("5") << "tst_qdir.cpp" << true; QTest::newRow("6") << "/resources.cpp" << false; QTest::newRow("resource0") << ":/prefix/foo.bar" << false; |