| 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
|