From c029d90d1975e124d237605f1edb2be16bd05b5d Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Tue, 28 Feb 2017 21:50:44 +0100 Subject: Initial commit --- src/paths.cc | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/paths.cc (limited to 'src/paths.cc') 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; +} -- cgit v1.2.3-70-g09d2