summaryrefslogtreecommitdiff
path: root/src/paths.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2017-02-28 21:50:44 +0100
committerJoel Klinghed <the_jk@yahoo.com>2017-02-28 21:50:44 +0100
commitc029d90d1975e124d237605f1edb2be16bd05b5d (patch)
tree9df87ffb365354bdb74a969440b32c8304bdbcb7 /src/paths.cc
Initial commit
Diffstat (limited to 'src/paths.cc')
-rw-r--r--src/paths.cc98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/paths.cc b/src/paths.cc
new file mode 100644
index 0000000..095076f
--- /dev/null
+++ b/src/paths.cc
@@ -0,0 +1,98 @@
+// -*- mode: c++; c-basic-offset: 2; -*-
+
+#include "common.hh"
+
+#include "paths.hh"
+
+// static
+std::string Paths::join(std::string const& base, std::string const& path) {
+ if (path.empty()) {
+ return cleanup(base);
+ } else if (path.front() == '/') {
+ return cleanup(path);
+ }
+ std::string ret(cleanup(base));
+ if (ret == ".") {
+ return cleanup(path);
+ }
+ if (ret.empty() || ret.back() != '/') {
+ ret.push_back('/');
+ }
+ ret.append(cleanup(path));
+ return ret;
+}
+
+namespace {
+
+std::string do_cleanup(std::string const& path) {
+ size_t i = 0, j = 0;
+ std::string ret;
+ bool add_slash = false;
+ if (path.front() == '/') {
+ ret.push_back('/');
+ i = j = 1;
+ }
+ while (true) {
+ if (j == path.size() || path[j] == '/') {
+ auto len = j - i;
+ if (len > 0) {
+ if (len == 1 && path[i] == '.') {
+ i = ++j;
+ continue;
+ }
+ if (add_slash && len == 2 && path[i] == '.' && path[i + 1] == '.') {
+ auto x = ret.find_last_of('/');
+ if (x == std::string::npos) x = 0;
+ ret.erase(x);
+ add_slash = !(ret.empty() || ret == "/");
+ i = ++j;
+ continue;
+ }
+ if (add_slash) {
+ ret.push_back('/');
+ } else {
+ add_slash = true;
+ }
+ ret.append(path.substr(i, len));
+ }
+ if (j == path.size()) {
+ return ret;
+ }
+ i = ++j;
+ } else {
+ ++j;
+ }
+ }
+}
+
+} // namespace
+
+// static
+std::string Paths::cleanup(std::string const& path) {
+ if (path.empty()) return ".";
+ if (path[0] == '.') {
+ if (path.size() == 1) return path;
+ if (path[1] == '/') return do_cleanup(path); // found ./ at beginning
+ }
+ for (size_t pos = 0; pos < path.size(); ++pos) {
+ if (path[pos] == '/') {
+ if (pos > 0 && pos + 1 == path.size()) {
+ // found slash at end
+ return path.substr(0, path.size() - 1);
+ }
+ if (path[pos + 1] == '/') return do_cleanup(path); // found double slash
+ if (path[pos + 1] == '.') {
+ if (pos + 2 == path.size()) return do_cleanup(path); // found /. at end
+ if (path[pos + 2] == '/') return do_cleanup(path); // found /./
+ if (path[pos + 2] == '.') {
+ if (pos + 3 == path.size()) {
+ // found /.. at end
+ return do_cleanup(path);
+ }
+ if (path[pos + 3] == '/') do_cleanup(path); // found /../
+ }
+ }
+ }
+ }
+ return path;
+}