diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 46aee5c895a..3e1c778e98b 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -2199,173 +2199,168 @@ bool QDir::match(const QString &filter, const QString &fileName) /*! \internal - - Updates \a path with redundant directory separators removed, and "."s and - ".."s resolved (as far as possible). It returns \c false if there were ".." - segments left over, attempt to go up past the root (only applies to - absolute paths), or \c true otherwise. + Returns \a path with redundant directory separators removed, + and "."s and ".."s resolved (as far as possible). This method is shared with QUrl, so it doesn't deal with QDir::separator(), nor does it remove the trailing slash, if any. - - When dealing with URLs, we are following section 5.2.4 (Remove dot - segments) from http://www.ietf.org/rfc/rfc3986.txt. URL mode differs from - from local path mode in these ways: - 1) it can set *path to empty ("." becomes "") - 2) directory path outputs end in / ("a/.." becomes "a/" instead of "a") - 3) a sequence of "//" is treated as multiple path levels ("a/b//.." becomes - "a/b/" and "a/b//../.." becomes "a/"), which matches the behavior - observed in web browsers. */ -bool qt_normalizePathSegments(QString *path, QDirPrivate::PathNormalizations flags) +QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormalizations flags, bool *ok) { const bool allowUncPaths = flags.testAnyFlag(QDirPrivate::AllowUncPaths); const bool isRemote = flags.testAnyFlag(QDirPrivate::RemotePath); - const qsizetype prefixLength = rootLength(*path, allowUncPaths); - - // RFC 3986 says: "The input buffer is initialized with the now-appended - // path components and the output buffer is initialized to the empty - // string." - const QChar *in = path->constBegin(); - - // Scan the input for a "." or ".." segment. If there isn't any, we may not - // need to modify this path at all. Also scan for "//" segments, which - // will be normalized if the path is local. - qsizetype i = prefixLength; - qsizetype n = path->size(); - for (bool lastWasSlash = true; i < n; ++i) { - if (lastWasSlash && in[i] == u'.') { - if (i + 1 == n || in[i + 1] == u'/') - break; - if (in[i + 1] == u'.' && (i + 2 == n || in[i + 2] == u'/')) - break; - } - if (!isRemote && lastWasSlash && in[i] == u'/' && i > 0) { - // backtrack one, so the algorithm below gobbles up the remaining - // slashes - --i; - break; - } - lastWasSlash = in[i] == u'/'; + const qsizetype len = name.size(); + + if (ok) + *ok = false; + + if (len == 0) + return name; + + qsizetype i = len - 1; + QVarLengthArray outVector(len); + qsizetype used = len; + char16_t *out = outVector.data(); + const char16_t *p = reinterpret_cast(name.data()); + const char16_t *prefix = p; + qsizetype up = 0; + + const qsizetype prefixLength = rootLength(name, allowUncPaths); + p += prefixLength; + i -= prefixLength; + + // replicate trailing slash (i > 0 checks for emptiness of input string p) + // except for remote paths because there can be /../ or /./ ending + if (i > 0 && p[i] == '/' && !isRemote) { + out[--used] = '/'; + --i; } - if (i == n) - return true; - QChar *out = path->data(); // detaches - const QChar *start = out + prefixLength; - const QChar *end = out + path->size(); - out += i; - in = out; - - // We implement a modified algorithm compared to RFC 3986, for efficiency. - bool ok = true; - do { -#if 0 // to see in the debugger - QString output = QStringView(path->constBegin(), out).toString(); - QStringView input(in, end); -#endif + auto isDot = [](const char16_t *p, qsizetype i) { + return i > 1 && p[i - 1] == '.' && p[i - 2] == '/'; + }; + auto isDotDot = [](const char16_t *p, qsizetype i) { + return i > 2 && p[i - 1] == '.' && p[i - 2] == '.' && p[i - 3] == '/'; + }; - // First, copy the preceding slashes, so we can look at the segment's - // content. If the path is part of a URL, we copy all slashes, otherwise - // just one. - if (in[0] == u'/') { - *out++ = *in++; - while (in < end && in[0] == u'/') { - if (isRemote) - *out++ = *in++; - else - ++in; - - // Note: we may exit this loop with in == end, in which case we - // *shouldn't* dereference *in. But since we are pointing to a - // detached, non-empty QString, we know there's a u'\0' at the - // end, so dereferencing is safe. + while (i >= 0) { + // copy trailing slashes for remote urls + if (p[i] == '/') { + if (isRemote && !up) { + if (isDot(p, i)) { + i -= 2; + continue; + } + out[--used] = p[i]; } + + --i; + continue; } - // Is this path segment either "." or ".."? - enum { Nothing, Dot, DotDot } type = Nothing; - if (in[0] == u'.') { - if (in + 1 == end || in[1] == u'/') - type = Dot; - else if (in[1] == u'.' && (in + 2 == end || in[2] == u'/')) - type = DotDot; + // remove current directory + if (p[i] == '.' && (i == 0 || p[i-1] == '/')) { + --i; + continue; } - if (type == Nothing) { - // If it is neither, then we copy this segment. - while (in < end && in[0] != u'/') - *out++ = *in++; + + // detect up dir + if (i >= 1 && p[i] == '.' && p[i-1] == '.' && (i < 2 || p[i - 2] == '/')) { + ++up; + i -= i >= 2 ? 3 : 2; + + if (isRemote) { + // moving up should consider empty path segments too (/path//../ -> /path/) + while (i > 0 && up && p[i] == '/') { + --up; + --i; + } + } continue; } - // Otherwise, we skip it and remove preceding slashes (if - // any, exactly one if part of a URL, all otherwise) from the - // output. If it is "..", we remove the segment before that and - // preceding slashes too in a similar fashion, if they are there. - if (type == DotDot) { - if (Q_UNLIKELY(out == start)) { - // we can't go further up from here, so we "re-root" - // without cleaning this segment - ok = false; - if (!isRemote) { - *out++ = u'.'; - *out++ = u'.'; - if (in + 2 != end) { - Q_ASSERT(in[2] == u'/'); - *out++ = u'/'; - ++in; + // prepend a slash before copying when not empty + if (!up && used != len && out[used] != '/') + out[--used] = '/'; + + // skip or copy + while (i >= 0) { + if (p[i] == '/') { + // copy all slashes as is for remote urls if they are not part of /./ or /../ + if (isRemote && !up) { + while (i > 0 && p[i] == '/' && !isDotDot(p, i)) { + + if (isDot(p, i)) { + i -= 2; + continue; + } + + out[--used] = p[i]; + --i; } - start = out; - in += 2; - continue; + + // in case of /./, jump over + if (isDot(p, i)) + i -= 2; + + break; } + + --i; + break; } - while (out > start && *--out != u'/') - ; - while (!isRemote && out > start && out[-1] == u'/') - --out; - while (out > start && out[-1] != u'/') - --out; - in += 2; // the two dots - } else { - ++in; // the one dot - } - if (out > start) { - // backtrack one or all the slashes (so "/tmp///" -> "/tmp/") - if (out[-1] == u'/' && in != end) - --out; - while (!isRemote && out > start && out[-1] == u'/') - --out; - } - if (out == start) { - // 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 - // absolute, into UNC. - // Note: this will turn ".//a" into "a" even for URLs! - if (in != end && in[0] == u'/') - ++in; - while (prefixLength == 0 && in != end && in[0] == u'/') - ++in; + // actual copy + if (!up) + out[--used] = p[i]; + --i; } - } while (in < end); - - path->truncate(out - path->constBegin()); - if (!isRemote && path->isEmpty()) - *path = u"."_s; - // we return false only if the path was absolute - return ok || prefixLength == 0; -} + // decrement up after copying/skipping + if (up) + --up; + } -QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormalizations flags, bool *ok) -{ - // temporary compat - QString copy = name; - bool r = qt_normalizePathSegments(©, flags); + // Indicate failure when ".." are left over for an absolute path. if (ok) - *ok = r; - return copy; + *ok = prefixLength == 0 || up == 0; + + // add remaining '..' + while (up && !isRemote) { + if (used != len && out[used] != '/') // is not empty and there isn't already a '/' + out[--used] = '/'; + out[--used] = '.'; + out[--used] = '.'; + --up; + } + + bool isEmpty = used == len; + + if (prefixLength) { + if (!isEmpty && out[used] == '/') { + // Even though there is a prefix the out string is a slash. This happens, if the input + // string only consists of a prefix followed by one or more slashes. Just skip the slash. + ++used; + } + for (qsizetype i = prefixLength - 1; i >= 0; --i) + out[--used] = prefix[i]; + } else { + if (isEmpty) { + // After resolving the input path, the resulting string is empty (e.g. "foo/.."). Return + // a dot in that case. + out[--used] = '.'; + } else if (out[used] == '/') { + // After parsing the input string, out only contains a slash. That happens whenever all + // parts are resolved and there is a trailing slash ("./" or "foo/../" for example). + // Prepend a dot to have the correct return value. + out[--used] = '.'; + } + } + + // If path was not modified return the original value + if (used == 0) + return name; + return QStringView(out + used, len - used).toString(); } static QString qt_cleanPath(const QString &path, bool *ok) diff --git a/src/corelib/io/qdir_p.h b/src/corelib/io/qdir_p.h index 908c1e8ed5b..7dce69c1952 100644 --- a/src/corelib/io/qdir_p.h +++ b/src/corelib/io/qdir_p.h @@ -81,7 +81,6 @@ class QDirPrivate : public QSharedData Q_DECLARE_OPERATORS_FOR_FLAGS(QDirPrivate::PathNormalizations) Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormalizations flags, bool *ok = nullptr); -bool qt_normalizePathSegments(QString *path, QDirPrivate::PathNormalizations flags); QT_END_NAMESPACE diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 407ae5ffce9..1599d25d345 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -911,7 +911,7 @@ inline void QUrlPrivate::appendPath(QString &appendTo, QUrl::FormattingOptions o { QString thePath = path; if (options & QUrl::NormalizePathSegments) { - qt_normalizePathSegments(&thePath, isLocalFile() ? QDirPrivate::DefaultNormalization : QDirPrivate::RemotePath); + thePath = qt_normalizePathSegments(path, isLocalFile() ? QDirPrivate::DefaultNormalization : QDirPrivate::RemotePath); } QStringView thePathView(thePath); @@ -1529,121 +1529,81 @@ inline QString QUrlPrivate::mergePaths(const QString &relativePath) const Removes unnecessary ../ and ./ from the path. Used for normalizing the URL. - - This code has a Qt-specific extension to handle empty path segments (a.k.a. - multiple slashes like "a//b"). We try to keep them wherever possible - because with some protocols they are meaningful, but we still consider them - to be a single directory transition for "." or ".." (e.g., "a/b//c" + - "../" is "a/"). See tst_QUrl::resolved() for the expected behavior. */ static void removeDotsFromPath(QString *path) { // The input buffer is initialized with the now-appended path // components and the output buffer is initialized to the empty // string. - const QChar *in = path->constBegin(); - - // Scan the input for a "." or ".." segment. If there isn't any, then we - // don't need to modify this path at all. - qsizetype i = 0, n = path->size(); - for (bool lastWasSlash = true; i < n; ++i) { - if (lastWasSlash && in[i] == u'.') { - if (i + 1 == n || in[i + 1] == u'/') - break; - if (in[i + 1] == u'.' && (i + 2 == n || in[i + 2] == u'/')) - break; - } - lastWasSlash = in[i] == u'/'; - } - if (i == n) - return; - QChar *out = path->data(); + const QChar *in = out; const QChar *end = out + path->size(); - out += i; - in = out; - - // We implement a modified algorithm compared to RFC 3986, for efficiency. - do { -#if 0 // to see in the debugger - QStringView output(path->constBegin(), out); - QStringView input(in, end); -#endif - // First, copy any preceding slashes, so we can look at the segment's - // content. - while (in < end && in[0] == u'/') { - *out++ = *in++; - - // Note: we may exit this loop with in == end, in which case we - // *shouldn't* dereference *in. But since we are pointing to a - // detached, non-empty QString, we know there's a u'\0' at the end. - } - // Is this path segment either "." or ".."? - enum { Nothing, Dot, DotDot } type = Nothing; - if (in[0] == u'.') { - if (in + 1 == end || in[1] == u'/') - type = Dot; - else if (in[1] == u'.' && (in + 2 == end || in[2] == u'/')) - type = DotDot; + // If the input buffer consists only of + // "." or "..", then remove that from the input + // buffer; + if (path->size() == 1 && in[0].unicode() == '.') + ++in; + else if (path->size() == 2 && in[0].unicode() == '.' && in[1].unicode() == '.') + in += 2; + // While the input buffer is not empty, loop: + while (in < end) { + + // otherwise, if the input buffer begins with a prefix of "../" or "./", + // then remove that prefix from the input buffer; + if (path->size() >= 2 && in[0].unicode() == '.' && in[1].unicode() == '/') + in += 2; + else if (path->size() >= 3 && in[0].unicode() == '.' + && in[1].unicode() == '.' && in[2].unicode() == '/') + in += 3; + + // otherwise, if the input buffer begins with a prefix of + // "/./" or "/.", where "." is a complete path segment, + // then replace that prefix with "/" in the input buffer; + if (in <= end - 3 && in[0].unicode() == '/' && in[1].unicode() == '.' + && in[2].unicode() == '/') { + in += 2; + continue; + } else if (in == end - 2 && in[0].unicode() == '/' && in[1].unicode() == '.') { + *out++ = u'/'; + in += 2; + break; } - if (type != Nothing) { - // If it is either, we skip it and remove any preceding slashes (if - // any) from the output. If it is "..", we remove the segment - // before that and its preceding slashes (if any) too. - const QChar *start = path->constBegin(); - if (type == DotDot) { - while (out > start && *--out != u'/') - ; - while (out > start && *--out == u'/') - ; - ++in; // the first dot - } - in += 2; // one dot and either one slash or the terminating null - while (out > start && *--out != u'/') + // otherwise, if the input buffer begins with a prefix + // of "/../" or "/..", where ".." is a complete path + // segment, then replace that prefix with "/" in the + // input buffer and remove the last //segment and its + // preceding "/" (if any) from the output buffer; + if (in <= end - 4 && in[0].unicode() == '/' && in[1].unicode() == '.' + && in[2].unicode() == '.' && in[3].unicode() == '/') { + while (out > path->constData() && (--out)->unicode() != '/') ; - - // And then replace the segment with "/", unless it would make a - // relative path become absolute. - if (out != start) { - // Replacing with a slash won't make the path absolute. - *out++ = u'/'; - } else if (*start == u'/') { - // The path is already absolute. - ++out; - } else { - // The path is relative, so we must skip any follow-on slashes - // to make sure the next iteration of the loop won't copy them, - // which would make the path become absolute. - while (in < end && *in == u'/') - ++in; - } + if (out == path->constData() && out->unicode() != '/') + ++in; + in += 3; continue; + } else if (in == end - 3 && in[0].unicode() == '/' && in[1].unicode() == '.' + && in[2].unicode() == '.') { + while (out > path->constData() && (--out)->unicode() != '/') + ; + if (out->unicode() == '/') + ++out; + in += 3; + break; } - // If it is neither, then we copy this segment. + // otherwise move the first path segment in + // the input buffer to the end of the output + // buffer, including the initial "/" character + // (if any) and any subsequent characters up + // to, but not including, the next "/" + // character or the end of the input buffer. + *out++ = *in++; while (in < end && in->unicode() != '/') *out++ = *in++; - } while (in < end); - path->truncate(out - path->constBegin()); -} - -// Authority-less URLs cannot have paths starting with double slashes (see -// QUrlPrivate::validityError). We refuse to turn a valid URL into invalid by -// way of QUrl::resolved(). -static void fixupNonAuthorityPath(QString *path) -{ - if (path->isEmpty() || path->at(0) != u'/') - return; - - // Find the first non-slash character, because its position is equal to the - // number of slashes. We'll remove all but one of them. - qsizetype i = 0; - while (i + 1 < path->size() && path->at(i + 1) == u'/') - ++i; - if (i) - path->remove(0, i); + } + path->truncate(out - path->constData()); } inline QUrlPrivate::ErrorCode QUrlPrivate::validityError(QString *source, qsizetype *position) const @@ -2818,8 +2778,6 @@ QUrl QUrl::resolved(const QUrl &relative) const t.d->sectionIsPresent &= ~QUrlPrivate::Fragment; removeDotsFromPath(&t.d->path); - if (!t.d->hasAuthority()) - fixupNonAuthorityPath(&t.d->path); #if defined(QURL_DEBUG) qDebug("QUrl(\"%ls\").resolved(\"%ls\") = \"%ls\"", diff --git a/tests/auto/corelib/io/qdir/tst_qdir.cpp b/tests/auto/corelib/io/qdir/tst_qdir.cpp index 8dd5ebca3cd..7b05a88e811 100644 --- a/tests/auto/corelib/io/qdir/tst_qdir.cpp +++ b/tests/auto/corelib/io/qdir/tst_qdir.cpp @@ -1385,22 +1385,17 @@ void tst_QDir::normalizePathSegments_data() QTest::newRow("data6") << "/./" << HandleUnc << "/"; QTest::newRow("data7") << "/.." << HandleUnc << "/.."; QTest::newRow("data8") << "/../" << HandleUnc << "/../"; - QTest::newRow("/../.") << "/../." << HandleUnc << "/../"; - QTest::newRow("/.././") << "/.././" << HandleUnc << "/../"; - QTest::newRow("/../..") << "/../.." << HandleUnc << "/../.."; QTest::newRow("data9") << "." << HandleUnc << "."; - QTest::newRow("data10") << "./" << HandleUnc << "."; + QTest::newRow("data10") << "./" << HandleUnc << "./"; QTest::newRow("data11") << "./." << HandleUnc << "."; - QTest::newRow("data12") << "././" << HandleUnc << "."; + QTest::newRow("data12") << "././" << HandleUnc << "./"; QTest::newRow("data13") << ".." << HandleUnc << ".."; QTest::newRow("data14") << "../" << HandleUnc << "../"; - QTest::newRow("data15") << "../." << HandleUnc << "../"; + QTest::newRow("data15") << "../." << HandleUnc << ".."; QTest::newRow("data16") << ".././" << HandleUnc << "../"; QTest::newRow("data17") << "../.." << HandleUnc << "../.."; QTest::newRow("data18") << "../../" << HandleUnc << "../../"; - QTest::newRow("./file1.txt") << "./file1.txt" << HandleUnc << "file1.txt"; QTest::newRow("data19") << ".//file1.txt" << HandleUnc << "file1.txt"; - QTest::newRow("/foo/bar//file1.txt") << "/foo/bar//file1.txt" << HandleUnc << "/foo/bar/file1.txt"; QTest::newRow("data20") << "/foo/bar/..//file1.txt" << HandleUnc << "/foo/file1.txt"; QTest::newRow("data21") << "foo/.." << HandleUnc << "."; QTest::newRow("data22") << "./foo/.." << HandleUnc << "."; @@ -1425,7 +1420,7 @@ void tst_QDir::normalizePathSegments_data() #else QTest::newRow("data37") << "c:/." << HandleUnc << "c:"; QTest::newRow("data38") << "c:/.." << HandleUnc << "."; - QTest::newRow("data39") << "c:/../" << HandleUnc << "."; + QTest::newRow("data39") << "c:/../" << HandleUnc << "./"; #endif QTest::newRow("data40") << "c:/./" << HandleUnc << "c:/"; QTest::newRow("data41") << "foo/../foo/.." << HandleUnc << "."; @@ -1452,9 +1447,10 @@ void tst_QDir::normalizePathSegments() QFETCH(QString, path); QFETCH(UncHandling, uncHandling); QFETCH(QString, expected); - // for QDirPrivate::RemotePath, see tst_QUrl::resolving QString cleaned = qt_normalizePathSegments(path, uncHandling == HandleUnc ? QDirPrivate::AllowUncPaths : QDirPrivate::DefaultNormalization); QCOMPARE(cleaned, expected); + if (path == expected) + QVERIFY2(path.isSharedWith(cleaned), "Strings are same but data is not shared"); } # endif //QT_BUILD_INTERNAL diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index c4a5c077c99..fc191559557 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -839,33 +839,7 @@ void tst_QUrl::resolving_data() QTest::addColumn("relativeUrl"); QTest::addColumn("resolvedUrl"); - // boundary cases - QTest::newRow("empty-on-empty") << "http://a" << "" << "http://a"; - QTest::newRow("empty-on-/") << "http://a/" << "" << "http://a/"; - QTest::newRow("empty-on-//") << "http://a//" << "" << "http://a//"; - QTest::newRow("empty-on-/.") << "http://a/." << "" << "http://a/"; - QTest::newRow("empty-on-/./") << "http://a/./" << "" << "http://a/"; - QTest::newRow("empty-on-/..") << "http://a/.." << "" << "http://a/"; - QTest::newRow("empty-on-/../") << "http://a/../" << "" << "http://a/"; - - QTest::newRow("/-on-empty-with-authority") << "http://a" << "/" << "http://a/"; - QTest::newRow(".-on-empty-with-authority") << "http://a" << "." << "http://a/"; - QTest::newRow("./-on-empty-with-authority") << "http://a" << "./" << "http://a/"; - QTest::newRow(".//-on-empty-with-authority") << "http://a" << ".//" << "http://a//"; - QTest::newRow("..-on-empty-with-authority") << "http://a" << ".." << "http://a/"; - QTest::newRow("../-on-empty-with-authority") << "http://a" << "../" << "http://a/"; - QTest::newRow("/-on-empty-no-authority") << "scheme:" << "/" << "scheme:/"; - QTest::newRow(".-on-empty-no-authority") << "scheme:" << "." << "scheme:"; - QTest::newRow("./-on-empty-no-authority") << "scheme:" << "./" << "scheme:"; - QTest::newRow(".//-on-empty-no-authority") << "scheme:" << "./" << "scheme:"; - QTest::newRow("..-on-empty-no-authority") << "scheme:" << ".." << "scheme:"; - QTest::newRow("../-on-empty-no-authority") << "scheme:" << "../" << "scheme:"; - - QTest::newRow("scheme-change") << "http://a" << "https://b" << "https://b"; - QTest::newRow("scheme-change-path") << "http://a/" << "scheme:" << "scheme:"; - // 5.4.1 Normal Examples (http://www.ietf.org/rfc/rfc3986.txt) - // URL paths not ending in / QTest::newRow("g:h") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g:h") << QString::fromLatin1("g:h"); QTest::newRow("g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("g") << QString::fromLatin1("http://a/b/c/g"); QTest::newRow("./g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("./g") << QString::fromLatin1("http://a/b/c/g"); @@ -883,62 +857,12 @@ void tst_QUrl::resolving_data() QTest::newRow("[empty]") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("") << QString::fromLatin1("http://a/b/c/d;p?q"); QTest::newRow(".") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1(".") << QString::fromLatin1("http://a/b/c/"); QTest::newRow("./") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("./") << QString::fromLatin1("http://a/b/c/"); - QTest::newRow(".//") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1(".//") << QString::fromLatin1("http://a/b/c//"); QTest::newRow("..") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("..") << QString::fromLatin1("http://a/b/"); QTest::newRow("../") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../") << QString::fromLatin1("http://a/b/"); - QTest::newRow("..//") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("..//") << QString::fromLatin1("http://a/b//"); QTest::newRow("../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../g") << QString::fromLatin1("http://a/b/g"); - QTest::newRow("..//g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("..//g") << QString::fromLatin1("http://a/b//g"); QTest::newRow("../..") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../..") << QString::fromLatin1("http://a/"); QTest::newRow("../../") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../") << QString::fromLatin1("http://a/"); - QTest::newRow("../..//") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../..//") << QString::fromLatin1("http://a//"); QTest::newRow("../../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../g") << QString::fromLatin1("http://a/g"); - QTest::newRow("../..//g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../..//g") << QString::fromLatin1("http://a//g"); - - // URL paths ending in / - QTest::newRow("g:h-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g:h") << QString::fromLatin1("g:h"); - QTest::newRow("g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g") << QString::fromLatin1("http://a/b/c/g"); - QTest::newRow("./g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("./g") << QString::fromLatin1("http://a/b/c/g"); - QTest::newRow("g/-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g/") << QString::fromLatin1("http://a/b/c/g/"); - QTest::newRow("/g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("/g") << QString::fromLatin1("http://a/g"); - QTest::newRow("//g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("//g") << QString::fromLatin1("http://g"); - QTest::newRow("?y-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("?y") << QString::fromLatin1("http://a/b/c/;p?y"); - QTest::newRow("g?y-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g?y") << QString::fromLatin1("http://a/b/c/g?y"); - QTest::newRow("#s-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("#s") << QString::fromLatin1("http://a/b/c/;p?q#s"); - QTest::newRow("g#s-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g#s") << QString::fromLatin1("http://a/b/c/g#s"); - QTest::newRow("g?y#s-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g?y#s") << QString::fromLatin1("http://a/b/c/g?y#s"); - QTest::newRow(";x-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1(";x") << QString::fromLatin1("http://a/b/c/;x"); - QTest::newRow("g;x-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g;x") << QString::fromLatin1("http://a/b/c/g;x"); - QTest::newRow("g;x?y#s-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("g;x?y#s") << QString::fromLatin1("http://a/b/c/g;x?y#s"); - QTest::newRow("[empty]-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("") << QString::fromLatin1("http://a/b/c/;p?q"); - QTest::newRow(".-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1(".") << QString::fromLatin1("http://a/b/c/"); - QTest::newRow("./-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("./") << QString::fromLatin1("http://a/b/c/"); - QTest::newRow(".//-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1(".//") << QString::fromLatin1("http://a/b/c//"); - QTest::newRow("..-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("..") << QString::fromLatin1("http://a/b/"); - QTest::newRow("../-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../") << QString::fromLatin1("http://a/b/"); - QTest::newRow("..//-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("..//") << QString::fromLatin1("http://a/b//"); - QTest::newRow("../g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../g") << QString::fromLatin1("http://a/b/g"); - QTest::newRow("..//g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("..//g") << QString::fromLatin1("http://a/b//g"); - QTest::newRow("../..-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../..") << QString::fromLatin1("http://a/"); - QTest::newRow("../../-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../../") << QString::fromLatin1("http://a/"); - QTest::newRow("../..//-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../..//") << QString::fromLatin1("http://a//"); - QTest::newRow("../../g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../../g") << QString::fromLatin1("http://a/g"); - QTest::newRow("../..//g-on-/") << QString::fromLatin1("http://a/b/c/;p?q") << QString::fromLatin1("../..//g") << QString::fromLatin1("http://a//g"); - - // URL paths ending in // - QTest::newRow(".-on-//") << "http://a/b/c//" << "." << "http://a/b/c//"; - QTest::newRow("./-on-//") << "http://a/b/c//" << "./" << "http://a/b/c//"; - QTest::newRow(".//-on-//") << "http://a/b/c//" << ".//" << "http://a/b/c///"; // weird but correct - QTest::newRow("..-on-//") << "http://a/b/c//" << ".." << "http://a/b/"; - QTest::newRow("../-on-//") << "http://a/b/c//" << "../" << "http://a/b/"; - QTest::newRow("..//-on-//") << "http://a/b/c//" << "..//" << "http://a/b//"; - QTest::newRow("../g-on-//") << "http://a/b/c//" << "../g" << "http://a/b/g"; - QTest::newRow("..//g-on-//") << "http://a/b/c//" << "..//g" << "http://a/b//g"; - QTest::newRow("../..-on-//") << "http://a/b/c//" << "../.." << "http://a/"; - QTest::newRow("../../-on-//") << "http://a/b/c//" << "../../" << "http://a/"; - QTest::newRow("../..//-on-//") << "http://a/b/c//" << "../..//" << "http://a//"; - QTest::newRow("../../g-on-//") << "http://a/b/c//" << "../../g" << "http://a/g"; - QTest::newRow("../..//g-on-//") << "http://a/b/c//" << "../..//g" << "http://a//g"; // 5.4.2 Abnormal Examples (http://www.ietf.org/rfc/rfc3986.txt) @@ -946,15 +870,8 @@ void tst_QUrl::resolving_data() // relative path ".." segments than there are hierarchical levels in the // base URI's path. Note that the ".." syntax cannot be used to change // the authority component of a URI. - QTest::newRow("../../../") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../") << QString::fromLatin1("http://a/"); - QTest::newRow("../../../..") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../..") << QString::fromLatin1("http://a/"); - QTest::newRow("../../../..//") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../..//") << QString::fromLatin1("http://a//"); - QTest::newRow("../../../../..") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../../..") << QString::fromLatin1("http://a/"); - QTest::newRow("../../../../../") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../../../") << QString::fromLatin1("http://a/"); QTest::newRow("../../../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../g") << QString::fromLatin1("http://a/g"); - QTest::newRow("../../..//g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../..//g") << QString::fromLatin1("http://a//g"); QTest::newRow("../../../../g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../../g") << QString::fromLatin1("http://a/g"); - QTest::newRow("../../../..//g") << QString::fromLatin1("http://a/b/c/d;p?q") << QString::fromLatin1("../../../..//g") << QString::fromLatin1("http://a//g"); // Similarly, parsers must remove the dot-segments "." and ".." when // they are complete components of a path, but not when they are only @@ -1000,49 +917,11 @@ void tst_QUrl::resolving_data() QTest::newRow("../a (2)") << QString::fromLatin1("b/a") << QString::fromLatin1("../a") << QString::fromLatin1("a"); QTest::newRow("../a (3)") << QString::fromLatin1("b/c/a") << QString::fromLatin1("../a") << QString::fromLatin1("b/a"); QTest::newRow("../a (4)") << QString::fromLatin1("b") << QString::fromLatin1("/a") << QString::fromLatin1("/a"); - QTest::newRow("relative+.") << "scheme:" << "." << "scheme:"; - QTest::newRow("relative+./") << "scheme:" << "./" << "scheme:"; - QTest::newRow("relative+.//") << "scheme:" << ".//" << "scheme:"; - QTest::newRow("relative+.///") << "scheme:" << ".///" << "scheme:"; - QTest::newRow("relative+./.") << "scheme:" << "./." << "scheme:"; - QTest::newRow("relative+././") << "scheme:" << "././" << "scheme:"; - QTest::newRow("relative+..") << "scheme:" << ".." << "scheme:"; - QTest::newRow("relative+../") << "scheme:" << "../" << "scheme:"; - QTest::newRow("relative+..//") << "scheme:" << "..//" << "scheme:"; - QTest::newRow("relative+..///") << "scheme:" << "..///" << "scheme:"; - QTest::newRow("relative+../.") << "scheme:" << "../." << "scheme:"; - QTest::newRow("relative+.././") << "scheme:" << ".././" << "scheme:"; - QTest::newRow("relative+.././/") << "scheme:" << ".././/" << "scheme:"; - QTest::newRow("relative+.././//") << "scheme:" << ".././//" << "scheme:"; - QTest::newRow("relative+../../../..") << "scheme:b/c/d" << "../../../.." << "scheme:"; - QTest::newRow("relative+../../../../") << "scheme:b/c/d" << "../../../../" << "scheme:"; - QTest::newRow("relative+../../../..//") << "scheme:b/c/d" << "../../../..//" << "scheme:"; - QTest::newRow("relative+../../d/../..") << "scheme:b/c/d" << "../../d/../.." << "scheme:"; - QTest::newRow("relative+../../d/../../") << "scheme:b/c/d" << "../../d/../../" << "scheme:"; - QTest::newRow("relative+endslash+../../../..") << "scheme:b/c/d/" << "../../../.." << "scheme:"; - QTest::newRow("relative+endslash+../../../../") << "scheme:b/c/d/" << "../../../../" << "scheme:"; - QTest::newRow("relative+endslash+../../../..//") << "scheme:b/c/d/" << "../../../..//" << "scheme:"; - - // Resolve absolute without authority with relative + QTest::newRow("../a (5)") << QString::fromLatin1("/b") << QString::fromLatin1("../a") << QString::fromLatin1("/a"); QTest::newRow("../a (6)") << QString::fromLatin1("/b/a") << QString::fromLatin1("../a") << QString::fromLatin1("/a"); QTest::newRow("../a (7)") << QString::fromLatin1("/b/c/a") << QString::fromLatin1("../a") << QString::fromLatin1("/b/a"); QTest::newRow("../a (8)") << QString::fromLatin1("/b") << QString::fromLatin1("/a") << QString::fromLatin1("/a"); - QTest::newRow("noauthority+.") << "scheme:/a/b" << "." << "scheme:/a/"; - QTest::newRow("noauthority+./") << "scheme:/a/b" << "./" << "scheme:/a/"; - QTest::newRow("noauthority+.//") << "scheme:/a/b" << ".//" << "scheme:/a//"; - QTest::newRow("noauthority+./d") << "scheme:/a/b" << "./d" << "scheme:/a/d"; - QTest::newRow("noauthority+.//d") << "scheme:/a/b" << ".//d" << "scheme:/a//d"; - QTest::newRow("noauthority+..") << "scheme:/a/b" << ".." << "scheme:/"; - QTest::newRow("noauthority+../") << "scheme:/a/b" << "../" << "scheme:/"; - QTest::newRow("noauthority+..//") << "scheme:/a/b" << "..//" << "scheme:/"; - QTest::newRow("noauthority+../d") << "scheme:/a/b" << "../d" << "scheme:/d"; - QTest::newRow("noauthority+..//d") << "scheme:/a/b" << "..//d" << "scheme:/d"; // no double slash! - QTest::newRow("noauthority+../..") << "scheme:/a/b" << "../.." << "scheme:/"; - QTest::newRow("noauthority+../../") << "scheme:/a/b" << "../../" << "scheme:/"; - QTest::newRow("noauthority+../..//") << "scheme:/a/b" << "../..//" << "scheme:/"; - QTest::newRow("noauthority+../../d") << "scheme:/a/b" << "../../d" << "scheme:/d"; - QTest::newRow("noauthority+../..//d") << "scheme:/a/b" << "../..//d" << "scheme:/d"; // no double slash! // More tests from KDE QTest::newRow("brackets") << QString::fromLatin1("http://www.calorieking.com/personal/diary/") << QString::fromLatin1("/personal/diary/rpc.php?C=jsrs1&F=getDiaryDay&P0=[2006-3-8]&U=1141858921458") << QString::fromLatin1("http://www.calorieking.com/personal/diary/rpc.php?C=jsrs1&F=getDiaryDay&P0=[2006-3-8]&U=1141858921458");