From a7bc4e849447758e1b67b1daefd0772f3f205ca6 Mon Sep 17 00:00:00 2001 From: David Faure Date: Thu, 25 Apr 2013 10:33:55 +0200 Subject: [PATCH] QUrl: add NormalizePathSegments to UrlFormattingOptions This is a bit like QDir::cleanPath(), but for URL paths. The code is shared with QDir::cleanPath(), by extracting the common parts it into a helper, qt_normalizePathSegments(). Change-Id: I7133c5e4aa2bf17fba98af13eb5371afba64197a Reviewed-by: Thiago Macieira --- src/corelib/io/qdir.cpp | 57 ++++++++++++++++--------- src/corelib/io/qurl.cpp | 8 +++- src/corelib/io/qurl.h | 3 +- tests/auto/corelib/io/qurl/tst_qurl.cpp | 11 +++++ 4 files changed, 56 insertions(+), 23 deletions(-) diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 9ca512e84f..9b3ea2fe2c 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -2005,25 +2005,14 @@ bool QDir::match(const QString &filter, const QString &fileName) #endif // QT_NO_REGEXP /*! - Returns \a path with directory separators normalized (converted to "/") and - redundant ones removed, and "."s and ".."s resolved (as far as possible). + Returns \a path with redundant directory separators removed, + and "."s and ".."s resolved (as far as possible). - Symbolic links are kept. This function does not return the - canonical path, but rather the simplest version of the input. - For example, "./local" becomes "local", "local/../bin" becomes - "bin" and "/local/usr/../bin" becomes "/local/bin". - - \sa absolutePath(), canonicalPath() + This method is shared with QUrl, so it doesn't deal with QDir::separator(), + nor does it remove the trailing slash, if any. */ -QString QDir::cleanPath(const QString &path) +QString qt_normalizePathSegments(const QString &name, bool allowUncPaths) { - if (path.isEmpty()) - return path; - QString name = path; - QChar dir_separator = separator(); - if (dir_separator != QLatin1Char('/')) - name.replace(dir_separator, QLatin1Char('/')); - int used = 0, levels = 0; const int len = name.length(); QVarLengthArray outVector(len); @@ -2033,10 +2022,8 @@ QString QDir::cleanPath(const QString &path) for (int i = 0, last = -1, iwrite = 0; i < len; ++i) { if (p[i] == QLatin1Char('/')) { while (i+1 < len && p[i+1] == QLatin1Char('/')) { -#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) //allow unc paths - if (!i) + if (allowUncPaths && i == 0) break; -#endif i++; } bool eaten = false; @@ -2099,8 +2086,6 @@ QString QDir::cleanPath(const QString &path) eaten = true; #endif last = -1; - } else if (last != -1 && i == len-1) { - eaten = true; } else { levels++; } @@ -2126,6 +2111,36 @@ QString QDir::cleanPath(const QString &path) } QString ret = (used == len ? name : QString(out, used)); + return ret; +} + +/*! + Returns \a path with directory separators normalized (converted to "/") and + redundant ones removed, and "."s and ".."s resolved (as far as possible). + + Symbolic links are kept. This function does not return the + canonical path, but rather the simplest version of the input. + For example, "./local" becomes "local", "local/../bin" becomes + "bin" and "/local/usr/../bin" becomes "/local/bin". + + \sa absolutePath(), canonicalPath() +*/ +QString QDir::cleanPath(const QString &path) +{ + if (path.isEmpty()) + return path; + QString name = path; + QChar dir_separator = separator(); + if (dir_separator != QLatin1Char('/')) + name.replace(dir_separator, QLatin1Char('/')); + + bool allowUncPaths = false; +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) //allow unc paths + allowUncPaths = true; +#endif + + QString ret = qt_normalizePathSegments(name, allowUncPaths); + // Strip away last slash except for root directories if (ret.length() > 1 && ret.endsWith(QLatin1Char('/'))) { #if defined (Q_OS_WIN) diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 16263ad736..b343a28e2f 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -231,6 +231,8 @@ \value PreferLocalFile If the URL is a local file according to isLocalFile() and contains no query or fragment, a local file path is returned. \value StripTrailingSlash The trailing slash is removed if one is present. + \value NormalizePathSegments Modifies the path to remove redundant directory separators, + and to resolve "."s and ".."s (as far as possible). Note that the case folding rules in \l{RFC 3491}{Nameprep}, which QUrl conforms to, require host names to always be converted to lower case, @@ -324,6 +326,7 @@ #endif QT_BEGIN_NAMESPACE +extern QString qt_normalizePathSegments(const QString &name, bool allowUncPaths); // qdir.cpp inline static bool isHex(char c) { @@ -803,6 +806,9 @@ inline void QUrlPrivate::appendPassword(QString &appendTo, QUrl::FormattingOptio inline void QUrlPrivate::appendPath(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const { QString thePath = path; + if (options & QUrl::NormalizePathSegments) { + thePath = qt_normalizePathSegments(path, false); + } if (options & QUrl::RemoveFilename) { const int slash = path.lastIndexOf(QLatin1Char('/')); if (slash == -1) @@ -3235,7 +3241,7 @@ QUrl QUrl::adjusted(QUrl::FormattingOptions options) const that.setFragment(QString()); if (options & RemovePath) { that.setPath(QString()); - } else if (options & (StripTrailingSlash | RemoveFilename)) { + } else if (options & (StripTrailingSlash | RemoveFilename | NormalizePathSegments)) { QString path; d->appendPath(path, options, QUrlPrivate::Path); that.setPath(path); diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h index 453e0be1d2..5678c7813b 100644 --- a/src/corelib/io/qurl.h +++ b/src/corelib/io/qurl.h @@ -141,7 +141,8 @@ public: // 0x100 was a private code in Qt 4, keep unused for a while PreferLocalFile = 0x200, StripTrailingSlash = 0x400, - RemoveFilename = 0x800 + RemoveFilename = 0x800, + NormalizePathSegments = 0x1000 }; enum ComponentFormattingOption { diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index 9b757ab934..c06aad38ff 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -298,6 +298,17 @@ void tst_QUrl::comparison() QEXPECT_FAIL("", "Normalization not implemented, will probably not be implemented like this", Continue); QCOMPARE(url3, url4); + QUrl url3bis = QUrl::fromEncoded("example://a/b/c/%7Bfoo%7D/"); + QUrl url3bisNoSlash = QUrl::fromEncoded("example://a/b/c/%7Bfoo%7D"); + QUrl url4bis = QUrl::fromEncoded("example://a/.//b/../b/c//%7Bfoo%7D/"); + QCOMPARE(url4bis.adjusted(QUrl::NormalizePathSegments), url3bis); + QCOMPARE(url4bis.adjusted(QUrl::NormalizePathSegments | QUrl::StripTrailingSlash), url3bisNoSlash); + + QUrl url4EncodedDots = QUrl("example://a/.//b/%2E%2E%2F/b/c/"); + QCOMPARE(QString::fromLatin1(url4EncodedDots.toEncoded()), QString::fromLatin1("example://a/.//b/..%2F/b/c/")); + QCOMPARE(url4EncodedDots.toString(), QString("example://a/.//b/..%2F/b/c/")); + QCOMPARE(url4EncodedDots.adjusted(QUrl::NormalizePathSegments).toString(), QString("example://a/b/..%2F/b/c/")); + // 6.2.2.1 Make sure hexdecimal characters in percent encoding are // treated case-insensitively QUrl url5;