| //===--------------------- filesystem/path.cpp ----------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is dual licensed under the MIT and the University of Illinois Open |
| // Source Licenses. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "experimental/filesystem" |
| #include "experimental/string_view" |
| #include "utility" |
| |
| _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM |
| |
| _LIBCPP_CONSTEXPR path::value_type path::preferred_separator; |
| |
| |
| namespace { namespace parser |
| { |
| |
| using string_type = string_view; |
| using value_type = path::value_type; |
| |
| using string_view_pair = pair<string_view, string_view>; |
| |
| // status reporting |
| constexpr size_t npos = static_cast<size_t>(-1); |
| |
| inline bool good(size_t pos) { return pos != npos; } |
| |
| // lexical elements |
| constexpr value_type preferred_separator = path::preferred_separator; |
| constexpr value_type const * preferred_separator_str = "/"; |
| constexpr value_type const * dot = "."; |
| |
| // forward // |
| bool is_separator(string_type const &, size_t); |
| bool is_root_name(const string_type&, size_t); |
| bool is_root_directory(string_type const &, size_t); |
| bool is_trailing_separator(string_type const &, size_t); |
| |
| size_t start_of(string_type const &, size_t); |
| size_t end_of(string_type const &, size_t); |
| |
| size_t root_name_start(const string_type& s); |
| size_t root_name_end(const string_type&); |
| |
| size_t root_directory_start(string_type const &); |
| size_t root_directory_end(string_type const &); |
| |
| string_view_pair separate_filename(string_type const &); |
| string_view extract_raw(string_type const &, size_t); |
| string_view extract_preferred(string_type const &, size_t); |
| |
| inline bool is_separator(const string_type& s, size_t pos) { |
| return (pos < s.size() && s[pos] == preferred_separator); |
| } |
| |
| inline bool is_root_name(const string_type& s, size_t pos) { |
| return good(pos) && pos == 0 ? root_name_start(s) == pos : false; |
| } |
| |
| inline bool is_root_directory(const string_type& s, size_t pos) { |
| return good(pos) ? root_directory_start(s) == pos : false; |
| } |
| |
| inline bool is_trailing_separator(const string_type& s, size_t pos) { |
| return (pos < s.size() && is_separator(s, pos) && |
| end_of(s, pos) == s.size()-1 && |
| !is_root_directory(s, pos) && !is_root_name(s, pos)); |
| } |
| |
| size_t start_of(const string_type& s, size_t pos) { |
| if (pos >= s.size()) return npos; |
| bool in_sep = (s[pos] == preferred_separator); |
| while (pos - 1 < s.size() && |
| (s[pos-1] == preferred_separator) == in_sep) |
| { --pos; } |
| if (pos == 2 && !in_sep && s[0] == preferred_separator && |
| s[1] == preferred_separator) |
| { return 0; } |
| return pos; |
| } |
| |
| size_t end_of(const string_type& s, size_t pos) { |
| if (pos >= s.size()) return npos; |
| // special case for root name |
| if (pos == 0 && is_root_name(s, pos)) return root_name_end(s); |
| bool in_sep = (s[pos] == preferred_separator); |
| while (pos + 1 < s.size() && (s[pos+1] == preferred_separator) == in_sep) |
| { ++pos; } |
| return pos; |
| } |
| |
| inline size_t root_name_start(const string_type& s) { |
| return good(root_name_end(s)) ? 0 : npos; |
| } |
| |
| size_t root_name_end(const string_type& s) { |
| if (s.size() < 2 || s[0] != preferred_separator |
| || s[1] != preferred_separator) { |
| return npos; |
| } |
| if (s.size() == 2) { |
| return 1; |
| } |
| size_t index = 2; // current position |
| if (s[index] == preferred_separator) { |
| return npos; |
| } |
| while (index + 1 < s.size() && s[index+1] != preferred_separator) { |
| ++index; |
| } |
| return index; |
| } |
| |
| size_t root_directory_start(const string_type& s) { |
| size_t e = root_name_end(s); |
| if (!good(e)) |
| return is_separator(s, 0) ? 0 : npos; |
| return is_separator(s, e + 1) ? e + 1 : npos; |
| } |
| |
| size_t root_directory_end(const string_type& s) { |
| size_t st = root_directory_start(s); |
| if (!good(st)) return npos; |
| size_t index = st; |
| while (index + 1 < s.size() && s[index + 1] == preferred_separator) |
| { ++index; } |
| return index; |
| } |
| |
| string_view_pair separate_filename(string_type const & s) { |
| if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""}; |
| auto pos = s.find_last_of('.'); |
| if (pos == string_type::npos) return string_view_pair{s, string_view{}}; |
| return string_view_pair{s.substr(0, pos), s.substr(pos)}; |
| } |
| |
| inline string_view extract_raw(const string_type& s, size_t pos) { |
| size_t end_i = end_of(s, pos); |
| if (!good(end_i)) return string_view{}; |
| return string_view(s).substr(pos, end_i - pos + 1); |
| } |
| |
| string_view extract_preferred(const string_type& s, size_t pos) { |
| string_view raw = extract_raw(s, pos); |
| if (raw.empty()) |
| return raw; |
| if (is_trailing_separator(s, pos)) |
| return string_view{dot}; |
| if (is_separator(s, pos) && !is_root_name(s, pos)) |
| return string_view(preferred_separator_str); |
| return raw; |
| } |
| |
| }} // namespace parser |
| |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // path_view_iterator |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace { |
| |
| struct path_view_iterator { |
| const string_view __s_; |
| size_t __pos_; |
| |
| explicit path_view_iterator(string_view const& __s) : __s_(__s), __pos_(__s_.empty() ? parser::npos : 0) {} |
| explicit path_view_iterator(string_view const& __s, size_t __p) : __s_(__s), __pos_(__p) {} |
| |
| string_view operator*() const { |
| return parser::extract_preferred(__s_, __pos_); |
| } |
| |
| path_view_iterator& operator++() { |
| increment(); |
| return *this; |
| } |
| |
| path_view_iterator& operator--() { |
| decrement(); |
| return *this; |
| } |
| |
| void increment() { |
| if (__pos_ == parser::npos) return; |
| while (! set_position(parser::end_of(__s_, __pos_)+1)) |
| ; |
| return; |
| } |
| |
| void decrement() { |
| if (__pos_ == 0) { |
| set_position(0); |
| } |
| else if (__pos_ == parser::npos) { |
| auto const str_size = __s_.size(); |
| set_position(parser::start_of( |
| __s_, str_size != 0 ? str_size - 1 : str_size)); |
| } else { |
| while (!set_position(parser::start_of(__s_, __pos_-1))) |
| ; |
| } |
| } |
| |
| bool set_position(size_t pos) { |
| if (pos >= __s_.size()) { |
| __pos_ = parser::npos; |
| } else { |
| __pos_ = pos; |
| } |
| return valid_iterator_position(); |
| } |
| |
| bool valid_iterator_position() const { |
| if (__pos_ == parser::npos) return true; // end position is valid |
| return (!parser::is_separator (__s_, __pos_) || |
| parser::is_root_directory (__s_, __pos_) || |
| parser::is_trailing_separator(__s_, __pos_) || |
| parser::is_root_name (__s_, __pos_)); |
| } |
| |
| bool is_end() const { return __pos_ == parser::npos; } |
| |
| inline bool operator==(path_view_iterator const& __p) { |
| return __pos_ == __p.__pos_; |
| } |
| }; |
| |
| path_view_iterator pbegin(path const& p) { |
| return path_view_iterator(p.native()); |
| } |
| |
| path_view_iterator pend(path const& p) { |
| path_view_iterator __p(p.native()); |
| __p.__pos_ = parser::npos; |
| return __p; |
| } |
| |
| } // end namespace |
| /////////////////////////////////////////////////////////////////////////////// |
| // path definitions |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| path & path::replace_extension(path const & replacement) |
| { |
| path p = extension(); |
| if (not p.empty()) { |
| __pn_.erase(__pn_.size() - p.native().size()); |
| } |
| if (!replacement.empty()) { |
| if (replacement.native()[0] != '.') { |
| __pn_ += "."; |
| } |
| __pn_.append(replacement.__pn_); |
| } |
| return *this; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // path.decompose |
| |
| string_view path::__root_name() const |
| { |
| return parser::is_root_name(__pn_, 0) |
| ? parser::extract_preferred(__pn_, 0) |
| : string_view{}; |
| } |
| |
| string_view path::__root_directory() const |
| { |
| auto start_i = parser::root_directory_start(__pn_); |
| if(!parser::good(start_i)) { |
| return {}; |
| } |
| return parser::extract_preferred(__pn_, start_i); |
| } |
| |
| string_view path::__relative_path() const |
| { |
| if (empty()) { |
| return {__pn_}; |
| } |
| auto end_i = parser::root_directory_end(__pn_); |
| if (not parser::good(end_i)) { |
| end_i = parser::root_name_end(__pn_); |
| } |
| if (not parser::good(end_i)) { |
| return {__pn_}; |
| } |
| return string_view(__pn_).substr(end_i+1); |
| } |
| |
| string_view path::__parent_path() const |
| { |
| if (empty() || pbegin(*this) == --pend(*this)) { |
| return {}; |
| } |
| auto end_it = --(--pend(*this)); |
| auto end_i = parser::end_of(__pn_, end_it.__pos_); |
| return string_view(__pn_).substr(0, end_i+1); |
| } |
| |
| string_view path::__filename() const |
| { |
| return empty() ? string_view{} : *--pend(*this); |
| } |
| |
| string_view path::__stem() const |
| { |
| return parser::separate_filename(__filename()).first; |
| } |
| |
| string_view path::__extension() const |
| { |
| return parser::separate_filename(__filename()).second; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // path.comparisons |
| int path::__compare(const value_type* __s) const { |
| path_view_iterator thisIter(this->native()); |
| path_view_iterator sIter(__s); |
| while (!thisIter.is_end() && !sIter.is_end()) { |
| int res = (*thisIter).compare(*sIter); |
| if (res != 0) return res; |
| ++thisIter; ++sIter; |
| } |
| if (thisIter.is_end() && sIter.is_end()) |
| return 0; |
| if (thisIter.is_end()) |
| return -1; |
| return 1; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // path.nonmembers |
| size_t hash_value(const path& __p) _NOEXCEPT { |
| path_view_iterator thisIter(__p.native()); |
| struct HashPairT { |
| size_t first; |
| size_t second; |
| }; |
| HashPairT hp = {0, 0}; |
| std::hash<string_view> hasher; |
| std::__scalar_hash<decltype(hp)> pair_hasher; |
| while (!thisIter.is_end()) { |
| hp.second = hasher(*thisIter); |
| hp.first = pair_hasher(hp); |
| ++thisIter; |
| } |
| return hp.first; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // path.itr |
| path::iterator path::begin() const |
| { |
| path_view_iterator pit = pbegin(*this); |
| iterator it; |
| it.__path_ptr_ = this; |
| it.__pos_ = pit.__pos_; |
| it.__elem_.__assign_view(*pit); |
| return it; |
| } |
| |
| path::iterator path::end() const |
| { |
| iterator it{}; |
| it.__path_ptr_ = this; |
| it.__pos_ = parser::npos; |
| return it; |
| } |
| |
| path::iterator& path::iterator::__increment() { |
| path_view_iterator it(__path_ptr_->native(), __pos_); |
| it.increment(); |
| __pos_ = it.__pos_; |
| __elem_.__assign_view(*it); |
| return *this; |
| } |
| |
| path::iterator& path::iterator::__decrement() { |
| path_view_iterator it(__path_ptr_->native(), __pos_); |
| it.decrement(); |
| __pos_ = it.__pos_; |
| __elem_.__assign_view(*it); |
| return *this; |
| } |
| |
| _LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM |