From 27f1f84c1c220c5dadc739d5b056ec0f1b9f63a6 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Tue, 22 May 2018 16:50:38 +0200 Subject: 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 --- src/corelib/io/qdir.cpp | 96 ++++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 36 deletions(-) (limited to 'src/corelib/io/qdir.cpp') 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('/'))) -- cgit v1.2.3