From 01b7f6ff090ddfac118f9f69c4b69df48898ef45 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 22 Mar 2019 06:11:24 +0800 Subject: [PATCH] add lexically_normal and weakly_canonical to nana.fs --- include/nana/filesystem/filesystem.hpp | 5 + source/filesystem/filesystem.cpp | 142 +++++++++++++++++++++++++ 2 files changed, 147 insertions(+) diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index beaa1545..64b37d97 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -290,6 +290,8 @@ namespace nana { namespace experimental { namespace filesystem // std::u16string generic_u16string() const; // std::u32string generic_u32string() const; + path lexically_normal() const; + //appends path& operator/=(const path& other); @@ -544,6 +546,9 @@ namespace std { path canonical(const path& p); path canonical(const path& p, std::error_code& err); + + path weakly_canonical(const path& p); + path weakly_canonical(const path& p, std::error_code& err); #endif #if defined(NANA_FILESYSTEM_FORCE) || defined(NANA_MINGW) diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index 061a12f7..16f3e448 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -225,9 +225,11 @@ namespace nana { namespace experimental { namespace filesystem //Because of No wide character version of POSIX #if defined(NANA_POSIX) const char* separators = "/"; + const char separator = '/'; const char* punt = "."; #else const wchar_t* separators = L"/\\"; + const char separator = '\\'; const wchar_t* punt = L"."; #endif @@ -568,6 +570,71 @@ namespace nana { namespace experimental { namespace filesystem std::replace(str.begin(), str.end(), '\\', '/'); // uppss ... revise this !!!!! return to_utf8(str); } + + path path::lexically_normal() const + { + if (pathstr_.empty()) + return *this; + + std::vector elements; + path temp{ pathstr_ }; + while (!temp.empty()) + { + elements.emplace_back(temp.filename()); + temp.remove_filename(); + } + + auto start = elements.begin(); + auto last = elements.end(); + auto stop = last--; + for (auto itr(start); itr != stop; ++itr) + { + // ignore "." except at start and last + if (itr->native().size() == 1 + && (itr->native())[0] == '.' + && itr != start + && itr != last) continue; + + // ignore a name and following ".." + if (!temp.empty() + && itr->native().size() == 2 + && (itr->native())[0] == '.' + && (itr->native())[1] == '.') // dot dot + { + string_type lf(temp.filename().native()); + if (lf.size() > 0 + && (lf.size() != 1 + || (lf[0] != '.' + && (lf[0] != '/' && lf[0] != '\\'))) + && (lf.size() != 2 + || (lf[0] != '.' + && lf[1] != '.' +# ifdef NANA_WINDOWS + && lf[1] != ':' +# endif + ) + ) + ) + { + temp.remove_filename(); + auto next = itr; + if (temp.empty() && ++next != stop + && next == last && last->string() == ".") + { + temp /= "."; + } + continue; + } + } + + temp /= *itr; + }; + + if (temp.empty()) + temp = "."; + return temp; + } + path & path::operator/=(const path& p) { if (p.empty()) @@ -1272,6 +1339,81 @@ namespace std { return canonical(p, &err); } + + bool try_throw(int err_val, const path& p, std::error_code* ec, const char* message) + { + if (0 == err_val) + { + if (ec) ec->clear(); + } + else + { //error + if (nullptr == ec) + throw (filesystem_error( + "nana::filesystem::canonical", p, + error_code(err_val, generic_category()))); + else + ec->assign(err_val, system_category()); + } + return err_val != 0; + } + + path weakly_canonical(const path& p, std::error_code* err) + { + path head{ p }; + + std::error_code tmp_err; + std::vector elements; + while (!head.empty()) + { + auto head_status = status(head, tmp_err); + + if (head_status.type() == file_type::unknown) + { + if (try_throw(static_cast(errc::invalid_argument), head, err, "nana::filesystem::weakly_canonical")) + return path{}; + } + if (head_status.type() != file_type::not_found) + break; + + elements.emplace_back(head.filename()); + head.remove_filename(); + } + + bool tail_has_dots = false; + path tail; + + for (auto & e : elements) + { + tail /= e; + // for a later optimization, track if any dot or dot-dot elements are present + if (e.native().size() <= 2 + && e.native()[0] == '.' + && (e.native().size() == 1 || e.native()[1] == '.')) + tail_has_dots = true; + } + + if (head.empty()) + return p.lexically_normal(); + head = canonical(head, tmp_err); + if (try_throw(tmp_err.value(), head, err, "nana::filesystem::weakly_canonical")) + return path(); + return tail.empty() + ? head + : (tail_has_dots // optimization: only normalize if tail had dot or dot-dot element + ? (head / tail).lexically_normal() + : head / tail); + } + + path weakly_canonical(const path& p) + { + return weakly_canonical(p, nullptr); + } + + path weakly_canonical(const path& p, std::error_code& err) + { + return weakly_canonical(p, &err); + } #endif #if defined(NANA_FILESYSTEM_FORCE) || defined(NANA_MINGW)