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 <thiago.macieira@intel.com>
This commit is contained in:
David Faure 2013-04-25 10:33:55 +02:00 committed by The Qt Project
parent 659f62981f
commit a7bc4e8494
4 changed files with 56 additions and 23 deletions

View File

@ -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<QChar> 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)

View File

@ -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);

View File

@ -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 {

View File

@ -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;