| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030 |
- /* 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>
- 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(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)
- {
- 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;
- cm::string_view const 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(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);
- }
- } // 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
- } // cm
- #else
- // Avoid empty translation unit.
- void cm_filesystem_path_cxx()
- {
- }
- #endif
|