// -*- 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; }