diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp index 64b37d97..86b907a6 100644 --- a/include/nana/filesystem/filesystem.hpp +++ b/include/nana/filesystem/filesystem.hpp @@ -108,11 +108,16 @@ namespace std { { using namespace experimental::filesystem; } + +#ifndef __cpp_lib_experimental_filesystem +# define __cpp_lib_experimental_filesystem 201406 +#endif } // std #else # undef NANA_USING_STD_FILESYSTEM # define NANA_USING_STD_FILESYSTEM 1 + //Detects whether the compiler supports std::filesystem under current options # if ((defined(_MSC_VER) && (_MSC_VER >= 1912) && defined(_MSVC_LANG) && _MSVC_LANG >= 201703)) || \ ((__cplusplus >= 201703L) && \ (defined(__clang__) && (__clang_major__ >= 7) || \ @@ -125,14 +130,11 @@ namespace std { using namespace std::experimental::filesystem; } } +# undef NANA_USING_STD_EXPERIMENTAL_FILESYSTEM +# define NANA_USING_STD_EXPERIMENTAL_FILESYSTEM # endif #endif - -#ifndef __cpp_lib_experimental_filesystem -# define __cpp_lib_experimental_filesystem 201406 -#endif - #if NANA_USING_NANA_FILESYSTEM #include @@ -558,7 +560,23 @@ namespace std { #endif } } // std +#else +//Implements the missing functions for various version of experimental/filesystem +# if defined(NANA_USING_STD_EXPERIMENTAL_FILESYSTEM) + namespace std + { + namespace filesystem + { + //Visual Studio 2017 + #if (defined(_MSC_VER) && (_MSC_VER > 1912)) || \ + (!defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 801)) + path weakly_canonical(const path& p); + path weakly_canonical(const path& p, std::error_code& err); + #endif + } + } +# endif #endif //NANA_USING_NANA_FILESYSTEM diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp index 16f3e448..96e1c8c5 100644 --- a/source/filesystem/filesystem.cpp +++ b/source/filesystem/filesystem.cpp @@ -1130,7 +1130,7 @@ namespace nana { namespace experimental { namespace filesystem ::fclose(stream); return bytes; } - ec.assign(static_cast(::errno), std::generic_category()); + ec.assign(static_cast(errno), std::generic_category()); #endif return static_cast(-1); } @@ -1265,7 +1265,7 @@ namespace std return p; // p.is_absolute() is true } - path absolute(const path& p, std::error_code& err) + path absolute(const path& p, std::error_code& /*err*/) { return absolute(p); } @@ -1350,7 +1350,7 @@ namespace std { //error if (nullptr == ec) throw (filesystem_error( - "nana::filesystem::canonical", p, + message, p, error_code(err_val, generic_category()))); else ec->assign(err_val, system_category()); @@ -1435,5 +1435,162 @@ namespace std }//end namespace filesystem }//end namespace std -#endif +#else //NANA_USING_NANA_FILESYSTEM +# if defined(NANA_USING_STD_EXPERIMENTAL_FILESYSTEM) + + //Defines the functions that are not provided by experimental/filesystem + namespace std + { + namespace filesystem + { + #if (defined(_MSC_VER) && (_MSC_VER > 1912)) || \ + (!defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 801)) + + namespace detail + { + 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( + message, p, + error_code(err_val, generic_category()))); + else + ec->assign(err_val, system_category()); + } + return err_val != 0; + } + + path lexically_normal(path p) + { + if (p.empty()) + return p; + + std::vector elements; + + while (!p.empty()) + { + elements.emplace_back(p.filename()); + p.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 (!p.empty() + && itr->native().size() == 2 + && (itr->native())[0] == '.' + && (itr->native())[1] == '.') // dot dot + { + auto lf(p.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 + ) + ) + ) + { + p.remove_filename(); + auto next = itr; + if (p.empty() && ++next != stop + && next == last && last->string() == ".") + { + p /= "."; + } + continue; + } + } + + p /= *itr; + }; + + if (p.empty()) + p = "."; + return p; + } + } + + 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 (detail::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 detail::lexically_normal(p); + head = canonical(head, tmp_err); + if (detail::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 + ? detail::lexically_normal(head / tail) + : 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 + } + } +# endif + +#endif //NANA_USING_NANA_FILESYSTEM