aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/str.cpp67
-rw-r--r--common/str.h32
-rw-r--r--test/common/str.h32
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");
+ }
};