| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029 | 
							- /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 
-    file Copyright.txt or https://cmake.org/licensing for details.  */
 
- #include <cm/filesystem> // IWYU pragma: associated
 
- #if !defined(CMake_HAVE_CXX_FILESYSTEM)
 
- #  include <algorithm>
 
- #  include <cassert>
 
- #  include <cstddef>
 
- #  include <cstdlib>
 
- #  include <functional>
 
- #  include <string>
 
- #  include <utility>
 
- #  include <vector>
 
- #  if defined(_WIN32) && !defined(__CYGWIN__)
 
- #    include <cctype>
 
- #  endif
 
- #  if defined(_WIN32) || defined(__CYGWIN__)
 
- #    include <iterator>
 
- #  endif
 
- #  include <cm/memory>
 
- #  include <cm/string_view>
 
- #  include <cmext/string_view>
 
- namespace cm {
 
- namespace filesystem {
 
- namespace internals {
 
- class path_parser
 
- {
 
- #  if defined(__SUNPRO_CC) && defined(__sparc)
 
-   // Oracle DeveloperStudio C++ compiler generates wrong code if enum size
 
-   // is different than the default.
 
-   using enum_size = int;
 
- #  else
 
-   using enum_size = unsigned char;
 
- #  endif
 
-   enum class state : enum_size
 
-   {
 
-     before_begin,
 
-     in_root_name,
 
-     in_root_dir,
 
-     in_filename,
 
-     trailing_separator,
 
-     at_end
 
-   };
 
-   using pointer = char const*;
 
- public:
 
-   enum class seek_position : enum_size
 
-   {
 
-     root_name = static_cast<enum_size>(state::in_root_name),
 
-     root_directory = static_cast<enum_size>(state::in_root_dir)
 
-   };
 
-   enum class peek_fragment : enum_size
 
-   {
 
-     remainder,
 
-     path
 
-   };
 
-   path_parser(cm::string_view path, bool set_at_end = false)
 
-     : State(set_at_end ? state::at_end : state::before_begin)
 
-     , Path(path)
 
-   {
 
-   }
 
-   path_parser(const path_parser&) = default;
 
-   ~path_parser() = default;
 
-   void reset() noexcept { this->set_state(state::before_begin); }
 
-   void increment() noexcept
 
-   {
 
-     const pointer start = this->next_token();
 
-     const pointer end = this->after_end();
 
-     if (start == end) {
 
-       this->set_state(state::at_end);
 
-       return;
 
-     }
 
-     switch (this->State) {
 
-       case state::before_begin: {
 
-         auto pos = this->consume_root_name(start, end);
 
-         if (pos) {
 
-           this->set_state(state::in_root_name);
 
-         } else {
 
-           pos = this->consume_separator(start, end);
 
-           if (pos) {
 
-             this->set_state(state::in_root_dir);
 
-           } else {
 
-             this->consume_filename(start, end);
 
-             this->set_state(state::in_filename);
 
-           }
 
-         }
 
-         break;
 
-       }
 
-       case state::in_root_name: {
 
-         auto pos = this->consume_separator(start, end);
 
-         if (pos) {
 
-           this->set_state(state::in_root_dir);
 
-         } else {
 
-           this->consume_filename(start, end);
 
-           this->set_state(state::in_filename);
 
-         }
 
-         break;
 
-       }
 
-       case state::in_root_dir: {
 
-         this->consume_filename(start, end);
 
-         this->set_state(state::in_filename);
 
-         break;
 
-       }
 
-       case state::in_filename: {
 
-         auto posSep = this->consume_separator(start, end);
 
-         if (posSep != end) {
 
-           auto pos = this->consume_filename(posSep, end);
 
-           if (pos) {
 
-             return;
 
-           }
 
-         }
 
-         set_state(state::trailing_separator);
 
-         break;
 
-       }
 
-       case state::trailing_separator: {
 
-         this->set_state(state::at_end);
 
-         break;
 
-       }
 
-       case state::at_end:
 
-         // unreachable
 
-         std::abort();
 
-     }
 
-   }
 
-   void decrement() noexcept
 
-   {
 
-     const pointer rstart = this->current_token() - 1;
 
-     const pointer rend = this->before_start();
 
-     if (rstart == rend) {
 
-       this->set_state(state::before_begin);
 
-       return;
 
-     }
 
-     switch (this->State) {
 
-       case state::at_end: {
 
-         auto posSep = this->consume_separator(rstart, rend);
 
-         if (posSep) {
 
-           if (posSep == rend) {
 
-             this->set_state(state::in_root_dir);
 
-           } else {
 
-             auto pos = this->consume_root_name(posSep, rend, true);
 
-             if (pos == rend) {
 
-               this->set_state(state::in_root_dir);
 
-             } else {
 
-               this->set_state(state::trailing_separator);
 
-             }
 
-           }
 
-         } else {
 
-           auto pos = this->consume_root_name(rstart, rend);
 
-           if (pos == rend) {
 
-             this->set_state(state::in_root_name);
 
-           } else {
 
-             this->consume_filename(rstart, rend);
 
-             this->set_state(state::in_filename);
 
-           }
 
-         }
 
-         break;
 
-       }
 
-       case state::trailing_separator: {
 
-         this->consume_filename(rstart, rend);
 
-         this->set_state(state::in_filename);
 
-         break;
 
-       }
 
-       case state::in_filename: {
 
-         auto posSep = this->consume_separator(rstart, rend);
 
-         if (posSep == rend) {
 
-           this->set_state(state::in_root_dir);
 
-         } else {
 
-           auto pos = this->consume_root_name(posSep, rend, true);
 
-           if (pos == rend) {
 
-             this->set_state(state::in_root_dir);
 
-           } else {
 
-             this->consume_filename(posSep, rend);
 
-             this->set_state(state::in_filename);
 
-           }
 
-         }
 
-         break;
 
-       }
 
-       case state::in_root_dir: {
 
-         auto pos = this->consume_root_name(rstart, rend);
 
-         if (pos) {
 
-           this->set_state(state::in_root_name);
 
-         }
 
-         break;
 
-       }
 
-       case state::in_root_name:
 
-       case state::before_begin: {
 
-         // unreachable
 
-         std::abort();
 
-       }
 
-     }
 
-   }
 
-   path_parser& operator++() noexcept
 
-   {
 
-     this->increment();
 
-     return *this;
 
-   }
 
-   path_parser& operator--() noexcept
 
-   {
 
-     this->decrement();
 
-     return *this;
 
-   }
 
-   cm::string_view operator*() const noexcept
 
-   {
 
-     switch (this->State) {
 
-       case state::before_begin:
 
-       case state::at_end:
 
-         return cm::string_view();
 
-       case state::trailing_separator:
 
-         return "";
 
-       case state::in_root_dir:
 
-       case state::in_root_name:
 
-       case state::in_filename:
 
-         return this->Entry;
 
-       default:
 
-         // unreachable
 
-         std::abort();
 
-     }
 
-   }
 
-   void seek(seek_position position)
 
-   {
 
-     state s = static_cast<state>(static_cast<int>(position));
 
-     while (this->State <= s) {
 
-       this->increment();
 
-     }
 
-   }
 
-   cm::string_view peek(peek_fragment fragment)
 
-   {
 
-     if (fragment == peek_fragment::remainder) {
 
-       // peek-up remain part of the initial path
 
-       return { this->Entry.data(),
 
-                std::size_t(&this->Path.back() - this->Entry.data() + 1) };
 
-     }
 
-     if (fragment == peek_fragment::path) {
 
-       // peek-up full path until current position
 
-       return { this->Path.data(),
 
-                std::size_t(&this->Entry.back() - this->Path.data() + 1) };
 
-     }
 
-     return {};
 
-   }
 
-   bool in_root_name() const { return this->State == state::in_root_name; }
 
-   bool in_root_directory() const { return this->State == state::in_root_dir; }
 
-   bool at_end() const { return this->State == state::at_end; }
 
-   bool at_start() const { return this->Entry.data() == this->Path.data(); }
 
- private:
 
-   void set_state(state newState) noexcept
 
-   {
 
-     this->State = newState;
 
-     if (newState == state::before_begin || newState == state::at_end) {
 
-       this->Entry = {};
 
-     }
 
-   }
 
-   pointer before_start() const noexcept { return this->Path.data() - 1; }
 
-   pointer after_end() const noexcept
 
-   {
 
-     return this->Path.data() + this->Path.size();
 
-   }
 
-   pointer current_token() const noexcept
 
-   {
 
-     switch (this->State) {
 
-       case state::before_begin:
 
-       case state::in_root_name:
 
-         return &this->Path.front();
 
-       case state::in_root_dir:
 
-       case state::in_filename:
 
-       case state::trailing_separator:
 
-         return &this->Entry.front();
 
-       case state::at_end:
 
-         return &this->Path.back() + 1;
 
-       default:
 
-         // unreachable
 
-         std::abort();
 
-     }
 
-   }
 
-   pointer next_token() const noexcept
 
-   {
 
-     switch (this->State) {
 
-       case state::before_begin:
 
-         return this->Path.data();
 
-       case state::in_root_name:
 
-       case state::in_root_dir:
 
-       case state::in_filename:
 
-         return &this->Entry.back() + 1;
 
-       case state::trailing_separator:
 
-       case state::at_end:
 
-         return after_end();
 
-       default:
 
-         // unreachable
 
-         std::abort();
 
-     }
 
-   }
 
-   pointer consume_separator(pointer ptr, pointer end) noexcept
 
-   {
 
-     if (ptr == end ||
 
-         (*ptr != '/'
 
- #  if defined(_WIN32)
 
-          && *ptr != '\\'
 
- #  endif
 
-          )) {
 
-       return nullptr;
 
-     }
 
-     const auto step = ptr < end ? 1 : -1;
 
-     ptr += step;
 
-     while (ptr != end &&
 
-            (*ptr == '/'
 
- #  if defined(_WIN32)
 
-             || *ptr == '\\'
 
- #  endif
 
-             )) {
 
-       ptr += step;
 
-     }
 
-     if (step == 1) {
 
-       this->Entry = cm::string_view(ptr - 1, 1);
 
-     } else {
 
-       this->Entry = cm::string_view(ptr + 1, 1);
 
-     }
 
-     return ptr;
 
-   }
 
-   pointer consume_filename(pointer ptr, pointer end) noexcept
 
-   {
 
-     auto start = ptr;
 
-     if (ptr == end || *ptr == '/'
 
- #  if defined(_WIN32)
 
-         || *ptr == '\\'
 
- #  endif
 
-     ) {
 
-       return nullptr;
 
-     }
 
-     const auto step = ptr < end ? 1 : -1;
 
-     ptr += step;
 
-     while (ptr != end && *ptr != '/'
 
- #  if defined(_WIN32)
 
-            && *ptr != '\\'
 
- #  endif
 
-     ) {
 
-       ptr += step;
 
-     }
 
- #  if defined(_WIN32)
 
-     if (step == -1 && (start - ptr) >= 2 && ptr == end) {
 
-       // rollback drive name consumption, if any
 
-       if (this->is_drive_name(ptr + 1)) {
 
-         ptr += 2;
 
-       }
 
-       if (ptr == start) {
 
-         return nullptr;
 
-       }
 
-     }
 
- #  endif
 
-     if (step == 1) {
 
-       this->Entry = cm::string_view(start, ptr - start);
 
-     } else {
 
-       this->Entry = cm::string_view(ptr + 1, start - ptr);
 
-     }
 
-     return ptr;
 
-   }
 
- #  if defined(_WIN32)
 
-   bool is_drive_name(pointer ptr)
 
-   {
 
-     return std::toupper(ptr[0]) >= 'A' && std::toupper(ptr[0]) <= 'Z' &&
 
-       ptr[1] == ':';
 
-   }
 
- #  endif
 
-   pointer consume_root_name(pointer ptr, pointer end,
 
-                             bool check_only = false) noexcept
 
-   {
 
- #  if defined(_WIN32) && !defined(__CYGWIN__)
 
-     if (ptr < end) {
 
-       if ((end - ptr) >= 2 && this->is_drive_name(ptr)) {
 
-         // Drive letter (X:) is a root name
 
-         if (!check_only) {
 
-           this->Entry = cm::string_view(ptr, 2);
 
-         }
 
-         return ptr + 2;
 
-       }
 
-       if ((end - ptr) > 2 && (ptr[0] == '/' || ptr[0] == '\\') &&
 
-           (ptr[1] == '/' || ptr[1] == '\\') &&
 
-           (ptr[2] != '/' && ptr[2] != '\\')) {
 
-         // server name (//server) is a root name
 
-         auto pos = std::find_if(ptr + 2, end,
 
-                                 [](char c) { return c == '/' || c == '\\'; });
 
-         if (!check_only) {
 
-           this->Entry = cm::string_view(ptr, pos - ptr);
 
-         }
 
-         return pos;
 
-       }
 
-     } else {
 
-       if ((ptr - end) >= 2 && this->is_drive_name(ptr - 1)) {
 
-         // Drive letter (X:) is a root name
 
-         if (!check_only) {
 
-           this->Entry = cm::string_view(ptr - 1, 2);
 
-         }
 
-         return ptr - 2;
 
-       }
 
-       if ((ptr - end) > 2 && (ptr[0] != '/' && ptr[0] != '\\')) {
 
-         std::reverse_iterator<pointer> start(ptr);
 
-         std::reverse_iterator<pointer> stop(end);
 
-         auto res = std::find_if(start, stop,
 
-                                 [](char c) { return c == '/' || c == '\\'; });
 
-         pointer pos = res.base() - 1;
 
-         if ((pos - 1) > end && (pos[-1] == '/' || pos[-1] == '\\')) {
 
-           // server name (//server) is a root name
 
-           if (!check_only) {
 
-             this->Entry = cm::string_view(pos - 1, ptr - pos + 2);
 
-           }
 
-           return pos - 2;
 
-         }
 
-       }
 
-     }
 
- #  elif defined(__CYGWIN__)
 
-     if (ptr < end) {
 
-       if ((end - ptr) > 2 && ptr[0] == '/' && ptr[1] == '/' && ptr[2] != '/') {
 
-         // server name (//server) is a root name
 
-         auto pos = std::find(ptr + 2, end, '/');
 
-         if (!check_only) {
 
-           this->Entry = cm::string_view(ptr, pos - ptr);
 
-         }
 
-         return pos;
 
-       }
 
-     } else {
 
-       if ((ptr - end) > 2 && ptr[0] != '/') {
 
-         std::reverse_iterator<pointer> start(ptr);
 
-         std::reverse_iterator<pointer> stop(end);
 
-         auto res = std::find(start, stop, '/');
 
-         pointer pos = res.base() - 1;
 
-         if ((pos - 1) > end && pos[-1] == '/') {
 
-           // server name (//server) is a root name
 
-           if (!check_only) {
 
-             this->Entry = cm::string_view(pos - 1, ptr - pos + 2);
 
-           }
 
-           return pos - 2;
 
-         }
 
-       }
 
-     }
 
- #  else
 
-     (void)ptr;
 
-     (void)end;
 
-     (void)check_only;
 
- #  endif
 
-     return nullptr;
 
-   }
 
-   state State;
 
-   const cm::string_view Path;
 
-   cm::string_view Entry;
 
- };
 
- // class unicode_helper
 
- void unicode_helper::append(std::string& str, std::uint32_t codepoint)
 
- {
 
-   if (codepoint <= 0x7f) {
 
-     str.push_back(static_cast<char>(codepoint));
 
-   } else if (codepoint >= 0x80 && codepoint <= 0x7ff) {
 
-     str.push_back(static_cast<char>((codepoint >> 6) + 192));
 
-     str.push_back(static_cast<char>((codepoint & 0x3f) + 128));
 
-   } else if ((codepoint >= 0x800 && codepoint <= 0xd7ff) ||
 
-              (codepoint >= 0xe000 && codepoint <= 0xffff)) {
 
-     str.push_back(static_cast<char>((codepoint >> 12) + 224));
 
-     str.push_back(static_cast<char>(((codepoint & 0xfff) >> 6) + 128));
 
-     str.push_back(static_cast<char>((codepoint & 0x3f) + 128));
 
-   } else if (codepoint >= 0x10000 && codepoint <= 0x10ffff) {
 
-     str.push_back(static_cast<char>((codepoint >> 18) + 240));
 
-     str.push_back(static_cast<char>(((codepoint & 0x3ffff) >> 12) + 128));
 
-     str.push_back(static_cast<char>(((codepoint & 0xfff) >> 6) + 128));
 
-     str.push_back(static_cast<char>((codepoint & 0x3f) + 128));
 
-   } else {
 
-     append(str, 0xfffd);
 
-   }
 
- }
 
- unicode_helper::utf8_state unicode_helper::decode(const utf8_state state,
 
-                                                   const std::uint8_t fragment,
 
-                                                   std::uint32_t& codepoint)
 
- {
 
-   const std::uint32_t utf8_state_info[] = {
 
-     // encoded states
 
-     0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u,
 
-     0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u,
 
-     0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu,
 
-     0x99999999u, 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u,
 
-     0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u,
 
-     0x88888888u, 0x88888883u, 0x88888885u, 0u,          0u,
 
-     0u,          0u,
 
-   };
 
-   std::uint8_t category = fragment < 128
 
-     ? 0
 
-     : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf;
 
-   codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu)
 
-                      : (0xffu >> category) & fragment);
 
-   return state == s_reject
 
-     ? s_reject
 
-     : static_cast<utf8_state>(
 
-         (utf8_state_info[category + 16] >> (state << 2)) & 0xf);
 
- }
 
- } // internals
 
- // Class path
 
- path& path::operator/=(const path& p)
 
- {
 
-   if (p.is_absolute() ||
 
-       (p.has_root_name() && p.get_root_name() != this->get_root_name())) {
 
-     this->path_ = p.path_;
 
-     return *this;
 
-   }
 
-   if (p.has_root_directory()) {
 
-     this->path_ = static_cast<std::string>(this->get_root_name());
 
-     this->path_ += static_cast<std::string>(p.get_root_directory());
 
-   } else if (this->has_filename()) {
 
-     this->path_ += this->preferred_separator;
 
- #  if defined(_WIN32) || defined(__CYGWIN__)
 
-     // special case: "//host" / "b" => "//host/b"
 
-   } else if (this->has_root_name() && !this->has_root_directory()) {
 
-     if (this->path_.length() >= 3 &&
 
-         (this->path_[0] == '/'
 
- #    if defined(_WIN32) && !defined(__CYGWIN__)
 
-          || this->path_[0] == '\\'
 
- #    endif
 
-          ) &&
 
-         (this->path_[1] == '/'
 
- #    if defined(_WIN32) && !defined(__CYGWIN__)
 
-          || this->path_[1] == '\\'
 
- #    endif
 
-          ) &&
 
-         (this->path_[2] != '/'
 
- #    if defined(_WIN32) && !defined(__CYGWIN__)
 
-          && this->path_[2] != '\\'
 
- #    endif
 
-          )) {
 
-       this->path_ += this->preferred_separator;
 
-     }
 
- #  endif
 
-   }
 
-   this->path_ += p.get_relative_path();
 
-   return *this;
 
- }
 
- path path::lexically_normal() const
 
- {
 
-   if (this->path_.empty()) {
 
-     return *this;
 
-   }
 
-   const cm::string_view dot = "."_s;
 
-   const cm::string_view dotdot = ".."_s;
 
-   std::vector<cm::string_view> root_parts;
 
-   std::vector<cm::string_view> parts;
 
-   bool root_directory_defined = false;
 
-   bool need_final_separator = false;
 
-   std::size_t path_size = 0;
 
-   internals::path_parser parser(this->path_);
 
-   ++parser;
 
-   while (!parser.at_end()) {
 
-     auto part = *parser;
 
-     if (parser.in_root_name() || parser.in_root_directory()) {
 
-       if (parser.in_root_directory()) {
 
-         root_directory_defined = true;
 
-       }
 
-       root_parts.push_back(part);
 
-       path_size += part.size();
 
-     } else if (part == dotdot) {
 
-       if (!parts.empty() && parts.back() != dotdot) {
 
-         need_final_separator = true;
 
-         path_size -= parts.back().size();
 
-         parts.pop_back();
 
-       } else if ((parts.empty() || parts.back() == dotdot) &&
 
-                  !root_directory_defined) {
 
-         parts.push_back(dotdot);
 
-         path_size += 2;
 
-       }
 
-     } else if (part == dot || part.empty()) {
 
-       need_final_separator = true;
 
-       if (part.empty()) {
 
-         parts.push_back(part);
 
-       }
 
-     } else {
 
-       // filename
 
-       need_final_separator = false;
 
-       parts.push_back(part);
 
-       path_size += part.size();
 
-     }
 
-     ++parser;
 
-   }
 
-   // no final separator if last element of path is ".."
 
-   need_final_separator =
 
-     need_final_separator && !parts.empty() && parts.back() != dotdot;
 
-   // build final path
 
-   //// compute final size of path
 
-   path_size += parts.size() + (need_final_separator ? 1 : 0);
 
-   std::string np;
 
-   np.reserve(path_size);
 
-   for (const auto& p : root_parts) {
 
-     np += p;
 
-   }
 
-   // convert any slash to the preferred_separator
 
-   if (static_cast<std::string::value_type>(this->preferred_separator) != '/') {
 
-     std::replace(
 
-       np.begin(), np.end(), '/',
 
-       static_cast<std::string::value_type>(this->preferred_separator));
 
-   }
 
-   for (const auto& p : parts) {
 
-     if (!p.empty()) {
 
-       np += p;
 
-       np += static_cast<std::string::value_type>(this->preferred_separator);
 
-     }
 
-   }
 
-   if (!parts.empty() && !need_final_separator) {
 
-     // remove extra separator
 
-     np.pop_back();
 
-   }
 
-   if (np.empty()) {
 
-     np.assign(1, '.');
 
-   }
 
-   return path(std::move(np));
 
- }
 
- path path::lexically_relative(const path& base) const
 
- {
 
-   internals::path_parser parser(this->path_);
 
-   ++parser;
 
-   internals::path_parser parserbase(base.path_);
 
-   ++parserbase;
 
-   cm::string_view this_root_name, base_root_name;
 
-   cm::string_view this_root_dir, base_root_dir;
 
-   if (parser.in_root_name()) {
 
-     this_root_name = *parser;
 
-     ++parser;
 
-   }
 
-   if (parser.in_root_directory()) {
 
-     this_root_dir = *parser;
 
-     ++parser;
 
-   }
 
-   if (parserbase.in_root_name()) {
 
-     base_root_name = *parserbase;
 
-     ++parserbase;
 
-   }
 
-   if (parserbase.in_root_directory()) {
 
-     base_root_dir = *parserbase;
 
-     ++parserbase;
 
-   }
 
-   auto is_path_absolute = [](cm::string_view rn, cm::string_view rd) -> bool {
 
- #  if defined(_WIN32) && !defined(__CYGWIN__)
 
-     return !rn.empty() && !rd.empty();
 
- #  else
 
-     (void)rn;
 
-     return !rd.empty();
 
- #  endif
 
-   };
 
-   if (this_root_name != base_root_name ||
 
-       is_path_absolute(this_root_name, this_root_dir) !=
 
-         is_path_absolute(base_root_name, base_root_dir) ||
 
-       (this_root_dir.empty() && !base_root_dir.empty())) {
 
-     return path();
 
-   }
 
- #  if defined(_WIN32) && !defined(__CYGWIN__)
 
-   // LWG3070 handle special case: filename can also be a root-name
 
-   auto is_drive_name = [](cm::string_view item) -> bool {
 
-     return item.length() == 2 && item[1] == ':';
 
-   };
 
-   parser.reset();
 
-   parser.seek(internals::path_parser::seek_position::root_directory);
 
-   while (!parser.at_end()) {
 
-     if (is_drive_name(*parser)) {
 
-       return path();
 
-     }
 
-     ++parser;
 
-   }
 
-   parserbase.reset();
 
-   parserbase.seek(internals::path_parser::seek_position::root_directory);
 
-   while (!parserbase.at_end()) {
 
-     if (is_drive_name(*parserbase)) {
 
-       return path();
 
-     }
 
-     ++parserbase;
 
-   }
 
- #  endif
 
-   const cm::string_view dot = "."_s;
 
-   const cm::string_view dotdot = ".."_s;
 
-   auto a = this->begin(), aend = this->end();
 
-   auto b = base.begin(), bend = base.end();
 
-   while (a != aend && b != bend && a->string() == b->string()) {
 
-     ++a;
 
-     ++b;
 
-   }
 
-   int count = 0;
 
-   for (; b != bend; ++b) {
 
-     auto part = *b;
 
-     if (part == dotdot) {
 
-       --count;
 
-     } else if (part.string() != dot && !part.empty()) {
 
-       ++count;
 
-     }
 
-   }
 
-   if (count == 0 && (a == this->end() || a->empty())) {
 
-     return path(dot);
 
-   }
 
-   if (count >= 0) {
 
-     path result;
 
-     path p_dotdot(dotdot);
 
-     for (int i = 0; i < count; ++i) {
 
-       result /= p_dotdot;
 
-     }
 
-     for (; a != aend; ++a) {
 
-       result /= *a;
 
-     }
 
-     return result;
 
-   }
 
-   // count < 0
 
-   return path();
 
- }
 
- path::path_type path::get_generic() const
 
- {
 
-   auto gen_path = this->path_;
 
-   auto start = gen_path.begin();
 
- #  if defined(_WIN32) && !defined(__CYGWIN__)
 
-   std::replace(gen_path.begin(), gen_path.end(), '\\', '/');
 
-   // preserve special syntax for root_name ('//server' or '//?')
 
-   if (gen_path.length() > 2 && gen_path[2] != '/') {
 
-     start += 2;
 
-   }
 
- #  endif
 
-   // remove duplicate separators
 
-   auto new_end = std::unique(start, gen_path.end(), [](char lhs, char rhs) {
 
-     return lhs == rhs && lhs == '/';
 
-   });
 
-   gen_path.erase(new_end, gen_path.end());
 
-   return gen_path;
 
- }
 
- cm::string_view path::get_root_name() const
 
- {
 
-   internals::path_parser parser(this->path_);
 
-   ++parser;
 
-   if (parser.in_root_name()) {
 
-     return *parser;
 
-   }
 
-   return {};
 
- }
 
- cm::string_view path::get_root_directory() const
 
- {
 
-   internals::path_parser parser(this->path_);
 
-   ++parser;
 
-   if (parser.in_root_name()) {
 
-     ++parser;
 
-   }
 
-   if (parser.in_root_directory()) {
 
-     return *parser;
 
-   }
 
-   return {};
 
- }
 
- cm::string_view path::get_relative_path() const
 
- {
 
-   internals::path_parser parser(this->path_);
 
-   parser.seek(internals::path_parser::seek_position::root_directory);
 
-   if (parser.at_end()) {
 
-     return {};
 
-   }
 
-   return parser.peek(internals::path_parser::peek_fragment::remainder);
 
- }
 
- cm::string_view path::get_parent_path() const
 
- {
 
-   if (!this->has_relative_path()) {
 
-     return this->path_;
 
-   }
 
-   // peek-up full path minus last element
 
-   internals::path_parser parser(this->path_, true);
 
-   --parser;
 
-   if (parser.at_start()) {
 
-     return {};
 
-   }
 
-   --parser;
 
-   return parser.peek(internals::path_parser::peek_fragment::path);
 
- }
 
- cm::string_view path::get_filename() const
 
- {
 
-   {
 
-     internals::path_parser parser(this->path_);
 
-     parser.seek(internals::path_parser::seek_position::root_directory);
 
-     if (parser.at_end()) {
 
-       return {};
 
-     }
 
-   }
 
-   {
 
-     internals::path_parser parser(this->path_, true);
 
-     return *(--parser);
 
-   }
 
- }
 
- cm::string_view path::get_filename_fragment(filename_fragment fragment) const
 
- {
 
-   auto file = this->get_filename();
 
-   if (file == "." || file == ".." || file.empty()) {
 
-     return fragment == filename_fragment::stem ? file : cm::string_view{};
 
-   }
 
-   auto pos = file.find_last_of('.');
 
-   if (pos == cm::string_view::npos || pos == 0) {
 
-     return fragment == filename_fragment::stem ? file : cm::string_view{};
 
-   }
 
-   return fragment == filename_fragment::stem ? file.substr(0, pos)
 
-                                              : file.substr(pos);
 
- }
 
- int path::compare_path(cm::string_view str) const
 
- {
 
-   internals::path_parser this_pp(this->path_);
 
-   ++this_pp;
 
-   internals::path_parser other_pp(str);
 
-   ++other_pp;
 
-   // compare root_name part
 
-   {
 
-     bool compare_root_names = false;
 
-     cm::string_view this_root_name, other_root_name;
 
-     int res;
 
-     if (this_pp.in_root_name()) {
 
-       compare_root_names = true;
 
-       this_root_name = *this_pp;
 
-       ++this_pp;
 
-     }
 
-     if (other_pp.in_root_name()) {
 
-       compare_root_names = true;
 
-       other_root_name = *other_pp;
 
-       ++other_pp;
 
-     }
 
-     if (compare_root_names &&
 
-         (res = this_root_name.compare(other_root_name) != 0)) {
 
-       return res;
 
-     }
 
-   }
 
-   // compare root_directory part
 
-   {
 
-     if (!this_pp.in_root_directory() && other_pp.in_root_directory()) {
 
-       return -1;
 
-     } else if (this_pp.in_root_directory() && !other_pp.in_root_directory()) {
 
-       return 1;
 
-     }
 
-     if (this_pp.in_root_directory()) {
 
-       ++this_pp;
 
-     }
 
-     if (other_pp.in_root_directory()) {
 
-       ++other_pp;
 
-     }
 
-   }
 
-   // compare various parts of the paths
 
-   while (!this_pp.at_end() && !other_pp.at_end()) {
 
-     int res;
 
-     if ((res = (*this_pp).compare(*other_pp)) != 0) {
 
-       return res;
 
-     }
 
-     ++this_pp;
 
-     ++other_pp;
 
-   }
 
-   // final step
 
-   if (this_pp.at_end() && !other_pp.at_end()) {
 
-     return -1;
 
-   } else if (!this_pp.at_end() && other_pp.at_end()) {
 
-     return 1;
 
-   }
 
-   return 0;
 
- }
 
- // Class path::iterator
 
- path::iterator::iterator()
 
-   : path_(nullptr)
 
- {
 
- }
 
- path::iterator::iterator(const iterator& other)
 
- {
 
-   this->path_ = other.path_;
 
-   if (other.parser_) {
 
-     this->parser_ = cm::make_unique<internals::path_parser>(*other.parser_);
 
-     this->path_element_ = path(**this->parser_);
 
-   }
 
- }
 
- path::iterator::iterator(const path* p, bool at_end)
 
-   : path_(p)
 
-   , parser_(cm::make_unique<internals::path_parser>(p->path_, at_end))
 
- {
 
-   if (!at_end) {
 
-     ++(*this->parser_);
 
-     this->path_element_ = path(**this->parser_);
 
-   }
 
- }
 
- path::iterator::~iterator() = default;
 
- path::iterator& path::iterator::operator=(const iterator& other)
 
- {
 
-   this->path_ = other.path_;
 
-   if (other.parser_) {
 
-     this->parser_ = cm::make_unique<internals::path_parser>(*other.parser_);
 
-     this->path_element_ = path(**this->parser_);
 
-   }
 
-   return *this;
 
- }
 
- path::iterator& path::iterator::operator++()
 
- {
 
-   assert(this->parser_);
 
-   if (this->parser_) {
 
-     assert(!this->parser_->at_end());
 
-     if (!this->parser_->at_end()) {
 
-       ++(*this->parser_);
 
-       if (this->parser_->at_end()) {
 
-         this->path_element_ = path();
 
-       } else {
 
-         this->path_element_ = path(**this->parser_);
 
-       }
 
-     }
 
-   }
 
-   return *this;
 
- }
 
- path::iterator& path::iterator::operator--()
 
- {
 
-   assert(this->parser_);
 
-   if (this->parser_) {
 
-     assert(!this->parser_->at_start());
 
-     if (!this->parser_->at_start()) {
 
-       --(*this->parser_);
 
-       this->path_element_ = path(**this->parser_);
 
-     }
 
-   }
 
-   return *this;
 
- }
 
- bool operator==(const path::iterator& lhs, const path::iterator& rhs)
 
- {
 
-   return lhs.path_ == rhs.path_ && lhs.parser_ != nullptr &&
 
-     ((lhs.parser_->at_end() && rhs.parser_->at_end()) ||
 
-      (lhs.parser_->at_start() && rhs.parser_->at_start()) ||
 
-      ((**lhs.parser_).data() == (**rhs.parser_).data()));
 
- }
 
- std::size_t hash_value(const path& p) noexcept
 
- {
 
-   internals::path_parser parser(p.path_);
 
-   std::hash<cm::string_view> hasher;
 
-   std::size_t value = 0;
 
-   while (!parser.at_end()) {
 
-     value = hasher(*parser) + 0x9e3779b9 + (value << 6) + (value >> 2);
 
-     ++parser;
 
-   }
 
-   return value;
 
- }
 
- } // filesystem
 
- } // cm
 
- #else
 
- // Avoid empty translation unit.
 
- void cm_filesystem_path_cxx()
 
- {
 
- }
 
- #endif
 
 
  |