Make sure QDir::absoluteFilePath("/dir") includes a drive on MS

QDir::isAbsolutePath(name) thinks any path starting with a slash is
absolute; however, to return a valid absolute path, we need to put a
drive prefix onto such a name. So use QFileSystemEntry::isAbsolute()
for that check (it believes in the need for a drive, or UNC prefix)
and handle the absolute-but-for-drive case when it arises.

Add a regression test and make related changes to existing tests.

Task-number: QTBUG-50839
Change-Id: Id5d2b2586bb1423fa2d9375a298a4bb5241cffe0
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Edward Welbourne 2017-07-28 13:25:50 +02:00
parent 3ae03c3585
commit 54190595ef
2 changed files with 47 additions and 7 deletions

View File

@ -750,13 +750,47 @@ QString QDir::filePath(const QString &fileName) const
QString QDir::absoluteFilePath(const QString &fileName) const
{
const QDirPrivate* d = d_ptr.constData();
if (isAbsolutePath(fileName))
// Don't trust our own isAbsolutePath(); Q_OS_WIN needs a drive.
if (QFileSystemEntry(fileName).isAbsolute())
return fileName;
d->resolveAbsoluteEntry();
const QString absoluteDirPath = d->absoluteDirEntry.filePath();
if (fileName.isEmpty())
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) {
// 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;
}
#endif // Q_OS_WIN
if (!absoluteDirPath.endsWith(QLatin1Char('/')))
return absoluteDirPath % QLatin1Char('/') % fileName;
return absoluteDirPath % fileName;

View File

@ -1384,16 +1384,22 @@ void tst_QDir::absoluteFilePath_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" << QDir::currentPath() + "/relative/path";
QTest::newRow("4") << "" << "" << QDir::currentPath();
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
QTest::newRow("5") << "//machine" << "share" << "//machine/share";
QTest::newRow("UNC") << "//machine" << "share" << "//machine/share";
QTest::newRow("Drive") << "c:/side/town" << "/my/way/home" << "c:/my/way/home";
#define DRIVE "Q:"
#else
#define DRIVE
#endif
QTest::newRow("0") << DRIVE "/etc" << "/passwd" << DRIVE "/passwd";
QTest::newRow("1") << DRIVE "/etc" << "passwd" << DRIVE "/etc/passwd";
QTest::newRow("2") << DRIVE "/" << "passwd" << DRIVE "/passwd";
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
}
void tst_QDir::absoluteFilePath()