| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file LICENSE.rst 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>
- #endif
- namespace cm {
- namespace filesystem {
- namespace internals {
- // class unicode_helper
- unicode_helper::utf8_state unicode_helper::decode(utf8_state const state,
- std::uint8_t const fragment,
- std::uint32_t& codepoint)
- {
- std::uint32_t const 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);
- }
- }
- }
- #if !defined(CMake_HAVE_CXX_FILESYSTEM)
- 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(path_parser const&) = default;
- ~path_parser() = default;
- void reset() noexcept { this->set_state(state::before_begin); }
- void increment() noexcept
- {
- pointer const start = this->next_token();
- pointer const 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
- {
- pointer const rstart = this->current_token() - 1;
- pointer const 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 ? posSep : rstart, rend,
- posSep != nullptr);
- if (pos == rend) {
- this->set_state(posSep ? state::in_root_dir : state::in_root_name);
- } 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;
- }
- auto const 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;
- }
- auto const 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)
- {
- auto ptr0 =
- static_cast<char>(std::toupper(static_cast<unsigned char>(ptr[0])));
- return ptr0 >= 'A' && ptr0 <= '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;
- cm::string_view const Path;
- cm::string_view Entry;
- };
- } // internals
- // Class path
- path& path::operator/=(path const& 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;
- }
- cm::string_view const dot = "."_s;
- cm::string_view const 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 (auto const& 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 (auto const& 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(path const& 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
- cm::string_view const dot = "."_s;
- cm::string_view const 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.empty() || file == "." || file == "..") {
- 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(iterator const& 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(path const* 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=(iterator const& 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==(path::iterator const& lhs, path::iterator const& 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(path const& 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
- #endif
- } // cm
|