QDir: change qt_normalizePathSegments to preserve trailing '/'s
... for local URLs too. For example normalizing "/Users/sam/troll/qt4.0/.." by following the alogrithm at https://www.ietf.org/rfc/rfc3986.html#section-5.2.4: - replace "/.." with a "/" in the input buffer, then remove the previous path segment from the output buffer up to, and including, the previous "/" - next iteration will find the input buffer starting with a "/" which is moved to the output buffer; the final result would be: "/Users/sam/troll/" Remove the KeepLocalTrailingSlash enumerator, it's become redundant with this change. De-duplicate a row in tst_QDir. [ChangeLog][Important Behavior Change] Aligned how QDir and QUrl normalize paths with respect to preserving a trailing slash. That is, QDir::cleanPath("/b/.") and QUrl("file:///b/.).toString(QUrl::NormalizePathSegments) will return "/b/" and "file:///b/" respectively. For more details see: https://www.ietf.org/rfc/rfc3986.html#section-5.2.4 Task-number: QTBUG-128940 Change-Id: Id5a4bdab14e7312a60b1b54de07e5d9f3e8aa40a Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
dc1d3abd7e
commit
ec894d6945
@ -2223,16 +2223,12 @@ bool QDir::match(const QString &filter, const QString &fileName)
|
|||||||
"a/b/" and "a/b//../.." becomes "a/"), which matches the behavior
|
"a/b/" and "a/b//../.." becomes "a/"), which matches the behavior
|
||||||
observed in web browsers.
|
observed in web browsers.
|
||||||
|
|
||||||
However, QUrl also uses local path mode for local URLs; with one exception:
|
As a Qt extension, for local URLs we treat multiple slashes as one slash.
|
||||||
Even in local mode we leave one trailing slash for paths ending in "/." if
|
|
||||||
the KeepLocalTrailingSlash flag is given. This reflects how QUrl needs to
|
|
||||||
treat local URLs due to compatibility constraints.
|
|
||||||
*/
|
*/
|
||||||
bool qt_normalizePathSegments(QString *path, QDirPrivate::PathNormalizations flags)
|
bool qt_normalizePathSegments(QString *path, QDirPrivate::PathNormalizations flags)
|
||||||
{
|
{
|
||||||
const bool allowUncPaths = flags.testAnyFlag(QDirPrivate::AllowUncPaths);
|
const bool allowUncPaths = flags.testAnyFlag(QDirPrivate::AllowUncPaths);
|
||||||
const bool isRemote = flags.testAnyFlag(QDirPrivate::RemotePath);
|
const bool isRemote = flags.testAnyFlag(QDirPrivate::RemotePath);
|
||||||
const bool keepLocalTrailingSlash = flags.testAnyFlags(QDirPrivate::KeepLocalTrailingSlash);
|
|
||||||
const qsizetype prefixLength = rootLength(*path, allowUncPaths);
|
const qsizetype prefixLength = rootLength(*path, allowUncPaths);
|
||||||
|
|
||||||
// RFC 3986 says: "The input buffer is initialized with the now-appended
|
// RFC 3986 says: "The input buffer is initialized with the now-appended
|
||||||
@ -2343,27 +2339,12 @@ bool qt_normalizePathSegments(QString *path, QDirPrivate::PathNormalizations fla
|
|||||||
++in; // the one dot
|
++in; // the one dot
|
||||||
}
|
}
|
||||||
|
|
||||||
if (out > start) {
|
// Not at 'end' yet, prepare for the next loop iteration by backtracking one slash.
|
||||||
// Always backtrack one slash
|
// E.g.: /a/b/../c >>> /a/b/../c
|
||||||
if (out[-1] == u'/' && in != end)
|
// ^out ^out
|
||||||
|
// the next iteration will copy '/c' to the output buffer >>> /a/c
|
||||||
|
if (in != end && out > start && out[-1] == u'/')
|
||||||
--out;
|
--out;
|
||||||
|
|
||||||
if (!isRemote) {
|
|
||||||
bool removedAnySlashes = false;
|
|
||||||
|
|
||||||
// Backtrack all slashes ...
|
|
||||||
while (out > start && out[-1] == u'/') {
|
|
||||||
--out;
|
|
||||||
removedAnySlashes = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... except a trailing one if it exists and flag given
|
|
||||||
if (removedAnySlashes && keepLocalTrailingSlash && out > start) {
|
|
||||||
++out;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (out == start) {
|
if (out == start) {
|
||||||
// We've reached the root. Make sure we don't turn a relative path
|
// We've reached the root. Make sure we don't turn a relative path
|
||||||
// to absolute or, in the case of local paths that are already
|
// to absolute or, in the case of local paths that are already
|
||||||
|
@ -31,7 +31,6 @@ public:
|
|||||||
DefaultNormalization = 0x00,
|
DefaultNormalization = 0x00,
|
||||||
AllowUncPaths = 0x01,
|
AllowUncPaths = 0x01,
|
||||||
RemotePath = 0x02,
|
RemotePath = 0x02,
|
||||||
KeepLocalTrailingSlash = 0x04,
|
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(PathNormalizations, PathNormalization)
|
Q_DECLARE_FLAGS(PathNormalizations, PathNormalization)
|
||||||
Q_FLAGS(PathNormalizations)
|
Q_FLAGS(PathNormalizations)
|
||||||
|
@ -913,7 +913,7 @@ inline void QUrlPrivate::appendPath(QString &appendTo, QUrl::FormattingOptions o
|
|||||||
if (options & QUrl::NormalizePathSegments) {
|
if (options & QUrl::NormalizePathSegments) {
|
||||||
qt_normalizePathSegments(
|
qt_normalizePathSegments(
|
||||||
&thePath,
|
&thePath,
|
||||||
isLocalFile() ? QDirPrivate::KeepLocalTrailingSlash : QDirPrivate::RemotePath);
|
isLocalFile() ? QDirPrivate::DefaultNormalization : QDirPrivate::RemotePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringView thePathView(thePath);
|
QStringView thePathView(thePath);
|
||||||
@ -2716,7 +2716,7 @@ QUrl QUrl::resolved(const QUrl &relative) const
|
|||||||
|
|
||||||
qt_normalizePathSegments(
|
qt_normalizePathSegments(
|
||||||
&t.d->path,
|
&t.d->path,
|
||||||
isLocalFile() ? QDirPrivate::KeepLocalTrailingSlash : QDirPrivate::RemotePath);
|
isLocalFile() ? QDirPrivate::DefaultNormalization : QDirPrivate::RemotePath);
|
||||||
if (!t.d->hasAuthority())
|
if (!t.d->hasAuthority())
|
||||||
fixupNonAuthorityPath(&t.d->path);
|
fixupNonAuthorityPath(&t.d->path);
|
||||||
|
|
||||||
|
@ -1384,8 +1384,14 @@ void tst_QDir::normalizePathSegments_data()
|
|||||||
QTest::addColumn<UncHandling>("uncHandling");
|
QTest::addColumn<UncHandling>("uncHandling");
|
||||||
QTest::addColumn<QString>("expected");
|
QTest::addColumn<QString>("expected");
|
||||||
|
|
||||||
QTest::newRow("data0") << "/Users/sam/troll/qt4.0//.." << HandleUnc << "/Users/sam/troll";
|
QTest::newRow("data0") << "/Users/sam/troll/qt4.0//.." << HandleUnc << "/Users/sam/troll/";
|
||||||
QTest::newRow("data1") << "/Users/sam////troll/qt4.0//.." << HandleUnc << "/Users/sam/troll";
|
QTest::newRow("data1") << "/Users/sam////troll/qt4.0//.." << HandleUnc << "/Users/sam/troll/";
|
||||||
|
QTest::newRow("data53") <<"/b//." << HandleUnc << "/b/";
|
||||||
|
QTest::newRow("data54") <<"/b//./" << HandleUnc << "/b/";
|
||||||
|
QTest::newRow("data55") <<"/b/." << HandleUnc << "/b/";
|
||||||
|
QTest::newRow("data56") <<"/b/./" << HandleUnc << "/b/";
|
||||||
|
QTest::newRow("data57") <<"/b" << HandleUnc << "/b";
|
||||||
|
|
||||||
QTest::newRow("data2") << "/" << HandleUnc << "/";
|
QTest::newRow("data2") << "/" << HandleUnc << "/";
|
||||||
QTest::newRow("data3") << "//" << HandleUnc << "//";
|
QTest::newRow("data3") << "//" << HandleUnc << "//";
|
||||||
QTest::newRow("data4") << "//" << IgnoreUnc << "/";
|
QTest::newRow("data4") << "//" << IgnoreUnc << "/";
|
||||||
@ -1426,12 +1432,11 @@ void tst_QDir::normalizePathSegments_data()
|
|||||||
QTest::newRow("data34") << "c://foo" << HandleUnc << "c:/foo";
|
QTest::newRow("data34") << "c://foo" << HandleUnc << "c:/foo";
|
||||||
QTest::newRow("data35") << "c:" << HandleUnc << "c:";
|
QTest::newRow("data35") << "c:" << HandleUnc << "c:";
|
||||||
QTest::newRow("data36") << "c:foo/bar" << IgnoreUnc << "c:foo/bar";
|
QTest::newRow("data36") << "c:foo/bar" << IgnoreUnc << "c:foo/bar";
|
||||||
#if defined Q_OS_WIN
|
|
||||||
QTest::newRow("data37") << "c:/." << HandleUnc << "c:/";
|
QTest::newRow("data37") << "c:/." << HandleUnc << "c:/";
|
||||||
|
#if defined Q_OS_WIN
|
||||||
QTest::newRow("data38") << "c:/.." << HandleUnc << "c:/..";
|
QTest::newRow("data38") << "c:/.." << HandleUnc << "c:/..";
|
||||||
QTest::newRow("data39") << "c:/../" << HandleUnc << "c:/../";
|
QTest::newRow("data39") << "c:/../" << HandleUnc << "c:/../";
|
||||||
#else
|
#else
|
||||||
QTest::newRow("data37") << "c:/." << HandleUnc << "c:";
|
|
||||||
QTest::newRow("data38") << "c:/.." << HandleUnc << ".";
|
QTest::newRow("data38") << "c:/.." << HandleUnc << ".";
|
||||||
QTest::newRow("data39") << "c:/../" << HandleUnc << ".";
|
QTest::newRow("data39") << "c:/../" << HandleUnc << ".";
|
||||||
#endif
|
#endif
|
||||||
@ -1439,9 +1444,9 @@ void tst_QDir::normalizePathSegments_data()
|
|||||||
QTest::newRow("data41") << "foo/../foo/.." << HandleUnc << ".";
|
QTest::newRow("data41") << "foo/../foo/.." << HandleUnc << ".";
|
||||||
QTest::newRow("data42") << "foo/../foo/../.." << HandleUnc << "..";
|
QTest::newRow("data42") << "foo/../foo/../.." << HandleUnc << "..";
|
||||||
QTest::newRow("data43") << "..foo.bar/foo" << HandleUnc << "..foo.bar/foo";
|
QTest::newRow("data43") << "..foo.bar/foo" << HandleUnc << "..foo.bar/foo";
|
||||||
QTest::newRow("data44") << ".foo./bar/.." << HandleUnc << ".foo.";
|
QTest::newRow("data44") << ".foo./bar/.." << HandleUnc << ".foo./";
|
||||||
QTest::newRow("data45") << "foo/..bar.." << HandleUnc << "foo/..bar..";
|
QTest::newRow("data45") << "foo/..bar.." << HandleUnc << "foo/..bar..";
|
||||||
QTest::newRow("data46") << "foo/.bar./.." << HandleUnc << "foo";
|
QTest::newRow("data46") << "foo/.bar./.." << HandleUnc << "foo/";
|
||||||
QTest::newRow("data47") << "//foo//bar" << HandleUnc << "//foo/bar";
|
QTest::newRow("data47") << "//foo//bar" << HandleUnc << "//foo/bar";
|
||||||
QTest::newRow("data48") << "..." << HandleUnc << "...";
|
QTest::newRow("data48") << "..." << HandleUnc << "...";
|
||||||
QTest::newRow("data49") << "foo/.../bar" << HandleUnc << "foo/.../bar";
|
QTest::newRow("data49") << "foo/.../bar" << HandleUnc << "foo/.../bar";
|
||||||
|
@ -1487,6 +1487,11 @@ void tst_QUrl::fromLocalFileNormalize_data()
|
|||||||
|
|
||||||
QTest::newRow("absolute-path") << QString::fromLatin1("/a.txt") << QString::fromLatin1("file:///a.txt") << QString::fromLatin1("file:///a.txt");
|
QTest::newRow("absolute-path") << QString::fromLatin1("/a.txt") << QString::fromLatin1("file:///a.txt") << QString::fromLatin1("file:///a.txt");
|
||||||
QTest::newRow("relative-path") << QString::fromLatin1("a.txt") << QString::fromLatin1("file:a.txt") << QString::fromLatin1("file:a.txt");
|
QTest::newRow("relative-path") << QString::fromLatin1("a.txt") << QString::fromLatin1("file:a.txt") << QString::fromLatin1("file:a.txt");
|
||||||
|
|
||||||
|
QTest::newRow("absolute-path-trailing-slash") << u"/b/"_s << u"file:///b/"_s << u"file:///b/"_s;
|
||||||
|
QTest::newRow("absolute-path-no-trailing-slash") << u"/b"_s << u"file:///b"_s << u"file:///b"_s;
|
||||||
|
QTest::newRow("absolute-path-2-trailing-slashes") << u"/b//"_s << u"file:///b//"_s << u"file:///b/"_s;
|
||||||
|
|
||||||
QTest::newRow("percent") << QString::fromLatin1("/a%.txt") << QString::fromLatin1("file:///a%25.txt")
|
QTest::newRow("percent") << QString::fromLatin1("/a%.txt") << QString::fromLatin1("file:///a%25.txt")
|
||||||
<< QString::fromLatin1("file:///a%25.txt");
|
<< QString::fromLatin1("file:///a%25.txt");
|
||||||
QTest::newRow("percent25") << QString::fromLatin1("/a%25.txt") << QString::fromLatin1("file:///a%2525.txt")
|
QTest::newRow("percent25") << QString::fromLatin1("/a%25.txt") << QString::fromLatin1("file:///a%2525.txt")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user