diff options
author | Oliver Wolff <oliver.wolff@digia.com> | 2014-09-25 14:25:42 +0200 |
---|---|---|
committer | Rainer Keller <rainer.keller@digia.com> | 2014-09-25 16:16:59 +0200 |
commit | eedefa28bd66123f7787e989a4e4bccbb09f20bc (patch) | |
tree | 94fd75d1785c797297d1a25e4a0b178531fe7930 /src/corelib/io/qdir.cpp | |
parent | cc3875c2e463be5cf126a18637295a0c56358eda (diff) |
Refactored qt_normalizePathSegments
There were several use cases that did not work with the old
implementation and it was not really readable.
Task-number: QTBUG-3472
Task-number: QTBUG-40067
Task-number: QTBUG-23892
Change-Id: I1e038792dc54cdc6f8d9bb59d80b11dd3c56fac6
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Diffstat (limited to 'src/corelib/io/qdir.cpp')
-rw-r--r-- | src/corelib/io/qdir.cpp | 194 |
1 files changed, 104 insertions, 90 deletions
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 926c5e7d7b..a5e189a825 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -2018,106 +2018,120 @@ bool QDir::match(const QString &filter, const QString &fileName) This method is shared with QUrl, so it doesn't deal with QDir::separator(), nor does it remove the trailing slash, if any. */ -QString qt_normalizePathSegments(const QString &name, bool allowUncPaths) +Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool allowUncPaths) { - int used = 0, levels = 0; const int len = name.length(); + + if (len == 0) + return name; + + int i = len - 1; QVarLengthArray<QChar> outVector(len); + int used = len; QChar *out = outVector.data(); - const QChar *p = name.unicode(); - for (int i = 0, last = -1, iwrite = 0; i < len; ++i) { - if (p[i] == QLatin1Char('/')) { - while (i+1 < len && p[i+1] == QLatin1Char('/')) { - if (allowUncPaths && i == 0) - break; - i++; - } - bool eaten = false; - if (i+1 < len && p[i+1] == QLatin1Char('.')) { - int dotcount = 1; - if (i+2 < len && p[i+2] == QLatin1Char('.')) - dotcount++; - if (i == len - dotcount - 1) { - if (dotcount == 1) { - break; - } else if (levels) { - if (last == -1) { - for (int i2 = iwrite-1; i2 >= 0; i2--) { - if (out[i2] == QLatin1Char('/')) { - last = i2; - break; - } - } - } - used -= iwrite - last - 1; - break; - } - } else if (p[i+dotcount+1] == QLatin1Char('/')) { - if (dotcount == 2 && levels) { - if (last == -1 || iwrite - last == 1) { - for (int i2 = (last == -1) ? (iwrite-1) : (last-1); i2 >= 0; i2--) { - if (out[i2] == QLatin1Char('/')) { - eaten = true; - last = i2; - break; - } - } - } else { - eaten = true; - } - if (eaten) { - levels--; - used -= iwrite - last; - iwrite = last; - last = -1; - } - } else if (dotcount == 2 && i > 0 && p[i - 1] != QLatin1Char('.')) { - eaten = true; - used -= iwrite - qMax(0, last); - iwrite = qMax(0, last); - last = -1; - ++i; - } else if (dotcount == 1) { - eaten = true; - } - if (eaten) - i += dotcount; - } else { - levels++; - } - } else if (last != -1 && iwrite - last == 1) { -#if defined(Q_OS_WIN) - eaten = (iwrite > 2); -#else - eaten = true; + const QChar *prefix = p; + int up = 0; + + int prefixLength = 0; + + if (allowUncPaths && len >= 2 && p[1].unicode() == '/' && p[0].unicode() == '/') { + // starts with double slash + prefixLength = 2; +#ifdef Q_OS_WIN + } else if (len >= 2 && p[1].unicode() == ':') { + // remember the drive letter + prefixLength = (len > 2 && p[2].unicode() == '/') ? 3 : 2; #endif - last = -1; - } else { - levels++; - } - if (!eaten) - last = i - (i - iwrite); - else - continue; - } else if (!i && p[i] == QLatin1Char('.')) { - int dotcount = 1; - if (len >= 1 && p[1] == QLatin1Char('.')) - dotcount++; - if (len >= dotcount && p[dotcount] == QLatin1Char('/')) { - if (dotcount == 1) { - i++; - while (i+1 < len-1 && p[i+1] == QLatin1Char('/')) - i++; - continue; - } + } else if (p[0].unicode() == '/') { + prefixLength = 1; + } + p += prefixLength; + i -= prefixLength; + + // replicate trailing slash (i > 0 checks for emptiness of input string p) + if (i > 0 && p[i].unicode() == '/') { + out[--used].unicode() = '/'; + --i; + } + + while (i >= 0) { + // remove trailing slashes + if (p[i].unicode() == '/') { + --i; + continue; + } + + // remove current directory + if (p[i].unicode() == '.' && (i == 0 || p[i-1].unicode() == '/')) { + --i; + continue; + } + + // detect up dir + if (i >= 1 && p[i].unicode() == '.' && p[i-1].unicode() == '.' + && (i == 1 || (i >= 2 && p[i-2].unicode() == '/'))) { + ++up; + i -= 2; + continue; + } + + // prepend a slash before copying when not empty + if (!up && used != len && out[used].unicode() != '/') + out[--used] = QLatin1Char('/'); + + // skip or copy + while (i >= 0) { + if (p[i].unicode() == '/') { // do not copy slashes + --i; + break; } + + // actual copy + if (!up) + out[--used] = p[i]; + --i; + } + + // decrement up after copying/skipping + if (up) + --up; + } + + // add remaining '..' + while (up) { + if (used != len && out[used].unicode() != '/') // is not empty and there isn't already a '/' + out[--used] = QLatin1Char('/'); + out[--used] = QLatin1Char('.'); + out[--used] = QLatin1Char('.'); + --up; + } + + bool isEmpty = used == len; + + if (prefixLength) { + if (!isEmpty && out[used].unicode() == '/') { + // Eventhough 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 (int 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] = QLatin1Char('.'); + } else if (out[used].unicode() == '/') { + // 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] = QLatin1Char('.'); } - out[iwrite++] = p[i]; - used++; } - QString ret = (used == len ? name : QString(out, used)); + // If path was not modified return the original value + QString ret = (used == 0 ? name : QString(out + used, len - used)); return ret; } |