diff options
-rw-r--r-- | common/str.cpp | 67 | ||||
-rw-r--r-- | common/str.h | 32 | ||||
-rw-r--r-- | test/common/str.h | 32 |
3 files changed, 131 insertions, 0 deletions
diff --git a/common/str.cpp b/common/str.cpp index c254d4bbc8..e70835a132 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -524,4 +524,71 @@ char *trim(char *t) { return rtrim(ltrim(t)); } +Common::String lastPathComponent(const Common::String &path, const char sep) { + const char *str = path.c_str(); + const char *last = str + path.size(); + + // Skip over trailing slashes + while (last > str && *(last-1) == sep) + --last; + + // Path consisted of only slashes -> return empty string + if (last == str) + return Common::String(); + + // Now scan the whole component + const char *first = last - 1; + while (first >= str && *first != sep) + --first; + + if (*first == sep) + first++; + + return Common::String(first, last); +} + +Common::String normalizePath(const Common::String &path, const char sep) { + if (path.empty()) + return path; + + const char *cur = path.c_str(); + Common::String result; + + // If there is a leading slash, preserve that: + if (*cur == sep) { + result += sep; + while (*cur == sep) + ++cur; + } + + // Scan till the end of the String + while (*cur != 0) { + const char *start = cur; + + // Scan till the next path separator resp. the end of the string + while (*cur != sep && *cur != 0) + cur++; + + const Common::String component(start, cur); + + // Skip empty components and dot components, add all others + if (!component.empty() && component != ".") { + // Add a separator before the component, unless the result + // string already ends with one (which happens only if the + // path *starts* with a separator). + if (!result.empty() && result.lastChar() != sep) + result += sep; + + // Add the component + result += component; + } + + // Skip over separator chars + while (*cur == sep) + cur++; + } + + return result; +} + } // End of namespace Common diff --git a/common/str.h b/common/str.h index 663a819e70..596bf341f9 100644 --- a/common/str.h +++ b/common/str.h @@ -227,6 +227,38 @@ extern char *ltrim(char *t); extern char *rtrim(char *t); extern char *trim(char *t); + +/** + * Returns the last component of a given path. + * + * Examples: + * /foo/bar.txt would return 'bar.txt' + * /foo/bar/ would return 'bar' + * /foo/./bar// would return 'bar' + * + * @param path the path of which we want to know the last component + * @param sep character used to separate path components + * @return The last component of the path. + */ +Common::String lastPathComponent(const Common::String &path, const char sep); + +/** + * Normalize a gien path to a canonical form. In particular: + * - trailing separators are removed: /foo/bar/ -> /foo/bar + * - double separators (= empty components) are removed: /foo//bar -> /foo/bar + * - dot components are removed: /foo/./bar -> /foo/bar + * + * @todo remove double dot components: /foo/baz/../bar -> /foo/bar + * + * @param path the path to normalize + * @param sep the separator token (usually '/' on Unix-style systems, or '\\' on Windows based stuff) + * @return the normalized path + */ +Common::String normalizePath(const Common::String &path, const char sep); + + + + class StringList : public Array<String> { public: void push_back(const char *str) { diff --git a/test/common/str.h b/test/common/str.h index 72d4df6f61..c4819520d8 100644 --- a/test/common/str.h +++ b/test/common/str.h @@ -157,4 +157,36 @@ class StringTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(str, "TEST IT, NOW! 42"); TS_ASSERT_EQUALS(str2, "Test it, NOW! 42"); } + + void test_lastPathComponent(void) { + TS_ASSERT(Common::lastPathComponent("/", '/') == ""); + TS_ASSERT(Common::lastPathComponent("/foo/bar", '/') == "bar"); + TS_ASSERT(Common::lastPathComponent("/foo//bar/", '/') == "bar"); + TS_ASSERT(Common::lastPathComponent("/foo/./bar", '/') == "bar"); + TS_ASSERT(Common::lastPathComponent("/foo//./bar//", '/') == "bar"); + TS_ASSERT(Common::lastPathComponent("/foo//.bar//", '/') == ".bar"); + + TS_ASSERT(Common::lastPathComponent("", '/') == ""); + TS_ASSERT(Common::lastPathComponent("foo/bar", '/') == "bar"); + TS_ASSERT(Common::lastPathComponent("foo//bar/", '/') == "bar"); + TS_ASSERT(Common::lastPathComponent("foo/./bar", '/') == "bar"); + TS_ASSERT(Common::lastPathComponent("foo//./bar//", '/') == "bar"); + TS_ASSERT(Common::lastPathComponent("foo//.bar//", '/') == ".bar"); + } + + void test_normalizePath(void) { + TS_ASSERT(Common::normalizePath("/", '/') == "/"); + TS_ASSERT(Common::normalizePath("/foo/bar", '/') == "/foo/bar"); + TS_ASSERT(Common::normalizePath("/foo//bar/", '/') == "/foo/bar"); + TS_ASSERT(Common::normalizePath("/foo/./bar", '/') == "/foo/bar"); + TS_ASSERT(Common::normalizePath("/foo//./bar//", '/') == "/foo/bar"); + TS_ASSERT(Common::normalizePath("/foo//.bar//", '/') == "/foo/.bar"); + + TS_ASSERT(Common::normalizePath("", '/') == ""); + TS_ASSERT(Common::normalizePath("foo/bar", '/') == "foo/bar"); + TS_ASSERT(Common::normalizePath("foo//bar/", '/') == "foo/bar"); + TS_ASSERT(Common::normalizePath("foo/./bar", '/') == "foo/bar"); + TS_ASSERT(Common::normalizePath("foo//./bar//", '/') == "foo/bar"); + TS_ASSERT(Common::normalizePath("foo//.bar//", '/') == "foo/.bar"); + } }; |