diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index b846d2cdc26..4c6ff82d3f0 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -560,6 +560,30 @@ QString FileName::toUserOutput() const return QDir::toNativeSeparators(toString()); } +QString FileName::fileName(int pathComponents) const +{ + if (pathComponents < 0) + return *this; + const QChar slash = QLatin1Char('/'); + QTC_CHECK(!endsWith(slash)); + int i = lastIndexOf(slash); + if (pathComponents == 0 || i == -1) + return mid(i + 1); + int component = i + 1; + // skip adjacent slashes + while (i > 0 && at(--i) == slash); + while (i >= 0 && --pathComponents >= 0) { + i = lastIndexOf(slash, i); + component = i + 1; + while (i > 0 && at(--i) == slash); + } + + // If there are no more slashes before the found one, return the entire string + if (i > 0 && lastIndexOf(slash, i) != -1) + return mid(component); + return *this; +} + /// \returns a bool indicating whether a file with this /// FileName exists. bool FileName::exists() const diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 20a989003b1..32e96e6dc25 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -73,6 +73,7 @@ public: static FileName fromUtf8(const char *filename, int filenameSize = -1); QString toString() const; QString toUserOutput() const; + QString fileName(int pathComponents = 0) const; bool exists() const; FileName parentDir() const; diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp index c672b6e1e11..fb509d19831 100644 --- a/tests/auto/utils/fileutils/tst_fileutils.cpp +++ b/tests/auto/utils/fileutils/tst_fileutils.cpp @@ -47,6 +47,8 @@ private slots: void parentDir(); void isChildOf_data(); void isChildOf(); + void fileName_data(); + void fileName(); }; void tst_fileutils::parentDir_data() @@ -132,5 +134,45 @@ void tst_fileutils::isChildOf() QCOMPARE(res, result); } +void tst_fileutils::fileName_data() +{ + QTest::addColumn("path"); + QTest::addColumn("components"); + QTest::addColumn("result"); + + QTest::newRow("empty 1") << "" << 0 << ""; + QTest::newRow("empty 2") << "" << 1 << ""; + QTest::newRow("basic") << "/foo/bar/baz" << 0 << "baz"; + QTest::newRow("2 parts") << "/foo/bar/baz" << 1 << "bar/baz"; + QTest::newRow("root no depth") << "/foo" << 0 << "foo"; + QTest::newRow("root full") << "/foo" << 1 << "/foo"; + QTest::newRow("root included") << "/foo/bar/baz" << 2 << "/foo/bar/baz"; + QTest::newRow("too many parts") << "/foo/bar/baz" << 5 << "/foo/bar/baz"; + QTest::newRow("windows root") << "C:/foo/bar/baz" << 2 << "C:/foo/bar/baz"; + QTest::newRow("smb share") << "//server/share/file" << 2 << "//server/share/file"; + QTest::newRow("no slashes") << "foobar" << 0 << "foobar"; + QTest::newRow("no slashes with depth") << "foobar" << 1 << "foobar"; + QTest::newRow("multiple slashes 1") << "/foo/bar////baz" << 0 << "baz"; + QTest::newRow("multiple slashes 2") << "/foo/bar////baz" << 1 << "bar////baz"; + QTest::newRow("multiple slashes 3") << "/foo////bar/baz" << 2 << "/foo////bar/baz"; + QTest::newRow("single char 1") << "/a/b/c" << 0 << "c"; + QTest::newRow("single char 2") << "/a/b/c" << 1 << "b/c"; + QTest::newRow("single char 3") << "/a/b/c" << 2 << "/a/b/c"; + QTest::newRow("slash at end 1") << "/a/b/" << 0 << ""; + QTest::newRow("slash at end 2") << "/a/b/" << 1 << "b/"; + QTest::newRow("slashes at end 1") << "/a/b//" << 0 << ""; + QTest::newRow("slashes at end 2") << "/a/b//" << 1 << "b//"; + QTest::newRow("root only 1") << "/" << 0 << ""; + QTest::newRow("root only 2") << "/" << 1 << "/"; +} + +void tst_fileutils::fileName() +{ + QFETCH(QString, path); + QFETCH(int, components); + QFETCH(QString, result); + QCOMPARE(FileName::fromString(path).fileName(components), result); +} + QTEST_APPLESS_MAIN(tst_fileutils) #include "tst_fileutils.moc"