forked from qt-creator/qt-creator
Utils: Make path cleaning platform agnostic
Unfortunately, QDir::cleanPath() only cleans according to the rules of the host system, which can be wrong in remote setups. As the implementation is not accessible/tweakable from the outside, copy the relevant code and remove the platform #ifdef's. Change-Id: Ife9a925412a12d3cef21ed3721a387c61c152ddf Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -1660,6 +1660,15 @@ FilePath FilePath::resolvePath(const QString &tail) const
|
||||
return resolvePath(tailPath);
|
||||
}
|
||||
|
||||
// Cleans path part similar to QDir::cleanPath()
|
||||
// - directory separators normalized (that is, platform-native
|
||||
// separators 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".
|
||||
|
||||
FilePath FilePath::cleanPath() const
|
||||
{
|
||||
return withNewPath(doCleanPath(path()));
|
||||
@@ -1670,9 +1679,153 @@ QTextStream &operator<<(QTextStream &s, const FilePath &fn)
|
||||
return s << fn.toString();
|
||||
}
|
||||
|
||||
static QString normalizePathSegmentHelper(const QString &name)
|
||||
{
|
||||
const int len = name.length();
|
||||
|
||||
if (len == 0)
|
||||
return name;
|
||||
|
||||
int i = len - 1;
|
||||
QVarLengthArray<char16_t> outVector(len);
|
||||
int used = len;
|
||||
char16_t *out = outVector.data();
|
||||
const ushort *p = reinterpret_cast<const ushort *>(name.data());
|
||||
const ushort *prefix = p;
|
||||
int up = 0;
|
||||
|
||||
const int prefixLength = name.at(0) == u'/' ? 1 : 0;
|
||||
|
||||
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] == '/') {
|
||||
out[--used] = '/';
|
||||
--i;
|
||||
}
|
||||
|
||||
while (i >= 0) {
|
||||
if (p[i] == '/') {
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove current directory
|
||||
if (p[i] == '.' && (i == 0 || p[i-1] == '/')) {
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// detect up dir
|
||||
if (i >= 1 && p[i] == '.' && p[i-1] == '.' && (i < 2 || p[i - 2] == '/')) {
|
||||
++up;
|
||||
i -= i >= 2 ? 3 : 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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] == '/') {
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
|
||||
// actual copy
|
||||
if (!up)
|
||||
out[--used] = p[i];
|
||||
--i;
|
||||
}
|
||||
|
||||
// decrement up after copying/skipping
|
||||
if (up)
|
||||
--up;
|
||||
}
|
||||
|
||||
// Indicate failure when ".." are left over for an absolute path.
|
||||
// if (ok)
|
||||
// *ok = prefixLength == 0 || up == 0;
|
||||
|
||||
// add remaining '..'
|
||||
while (up) {
|
||||
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 (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] = '.';
|
||||
} 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 QString::fromUtf16(out + used, len - used);
|
||||
}
|
||||
|
||||
QString doCleanPath(const QString &input_)
|
||||
{
|
||||
QString input = input_;
|
||||
if (input.contains('\\'))
|
||||
input.replace('\\', '/');
|
||||
|
||||
if (input.startsWith("//?/")) {
|
||||
input = input.mid(4);
|
||||
if (input.startsWith("UNC/"))
|
||||
input = '/' + input.mid(3); // trick it into reporting two slashs at start
|
||||
}
|
||||
|
||||
int prefixLen = 0;
|
||||
const int shLen = FilePath::schemeAndHostLength(input);
|
||||
if (shLen > 0) {
|
||||
prefixLen = shLen + FilePath::rootLength(input.mid(shLen));
|
||||
} else {
|
||||
prefixLen = FilePath::rootLength(input);
|
||||
if (prefixLen > 0 && input.at(prefixLen - 1) == '/')
|
||||
--prefixLen;
|
||||
}
|
||||
|
||||
QString path = normalizePathSegmentHelper(input.mid(prefixLen));
|
||||
|
||||
// Strip away last slash except for root directories
|
||||
if (path.size() > 1 && path.endsWith(u'/'))
|
||||
path.chop(1);
|
||||
|
||||
return input.left(prefixLen) + path;
|
||||
}
|
||||
|
||||
|
||||
// FileFilter
|
||||
|
||||
FileFilter::FileFilter(const QStringList &nameFilters,
|
||||
const QDir::Filters fileFilters,
|
||||
const QDirIterator::IteratorFlags flags)
|
||||
const QDir::Filters fileFilters,
|
||||
const QDirIterator::IteratorFlags flags)
|
||||
: nameFilters(nameFilters),
|
||||
fileFilters(fileFilters),
|
||||
iteratorFlags(flags)
|
||||
|
||||
Reference in New Issue
Block a user