fs_path.cxx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include <cm/filesystem> // IWYU pragma: associated
  4. #if !defined(CMake_HAVE_CXX_FILESYSTEM)
  5. # include <algorithm>
  6. # include <cassert>
  7. # include <cstddef>
  8. # include <cstdlib>
  9. # include <functional>
  10. # include <string>
  11. # include <utility>
  12. # include <vector>
  13. # if defined(_WIN32)
  14. # include <cctype>
  15. # include <iterator>
  16. # endif
  17. # include <cm/memory>
  18. # include <cm/string_view>
  19. # include <cmext/string_view>
  20. namespace cm {
  21. namespace filesystem {
  22. namespace internals {
  23. class path_parser
  24. {
  25. # if defined(__SUNPRO_CC) && defined(__sparc)
  26. // Oracle DeveloperStudio C++ compiler generates wrong code if enum size
  27. // is different than the default.
  28. using enum_size = int;
  29. # else
  30. using enum_size = unsigned char;
  31. # endif
  32. enum class state : enum_size
  33. {
  34. before_begin,
  35. in_root_name,
  36. in_root_dir,
  37. in_filename,
  38. trailing_separator,
  39. at_end
  40. };
  41. using pointer = char const*;
  42. public:
  43. enum class seek_position : enum_size
  44. {
  45. root_name = static_cast<enum_size>(state::in_root_name),
  46. root_directory = static_cast<enum_size>(state::in_root_dir)
  47. };
  48. enum class peek_fragment : enum_size
  49. {
  50. remainder,
  51. path
  52. };
  53. path_parser(cm::string_view path, bool set_at_end = false)
  54. : State(set_at_end ? state::at_end : state::before_begin)
  55. , Path(path)
  56. {
  57. }
  58. path_parser(const path_parser&) = default;
  59. ~path_parser() = default;
  60. void reset() noexcept { this->set_state(state::before_begin); }
  61. void increment() noexcept
  62. {
  63. const pointer start = this->next_token();
  64. const pointer end = this->after_end();
  65. if (start == end) {
  66. this->set_state(state::at_end);
  67. return;
  68. }
  69. switch (this->State) {
  70. case state::before_begin: {
  71. auto pos = this->consume_root_name(start, end);
  72. if (pos) {
  73. this->set_state(state::in_root_name);
  74. } else {
  75. pos = this->consume_separator(start, end);
  76. if (pos) {
  77. this->set_state(state::in_root_dir);
  78. } else {
  79. this->consume_filename(start, end);
  80. this->set_state(state::in_filename);
  81. }
  82. }
  83. break;
  84. }
  85. case state::in_root_name: {
  86. auto pos = this->consume_separator(start, end);
  87. if (pos) {
  88. this->set_state(state::in_root_dir);
  89. } else {
  90. this->consume_filename(start, end);
  91. this->set_state(state::in_filename);
  92. }
  93. break;
  94. }
  95. case state::in_root_dir: {
  96. this->consume_filename(start, end);
  97. this->set_state(state::in_filename);
  98. break;
  99. }
  100. case state::in_filename: {
  101. auto posSep = this->consume_separator(start, end);
  102. if (posSep != end) {
  103. auto pos = this->consume_filename(posSep, end);
  104. if (pos) {
  105. return;
  106. }
  107. }
  108. set_state(state::trailing_separator);
  109. break;
  110. }
  111. case state::trailing_separator: {
  112. this->set_state(state::at_end);
  113. break;
  114. }
  115. case state::at_end:
  116. // unreachable
  117. std::abort();
  118. }
  119. }
  120. void decrement() noexcept
  121. {
  122. const pointer rstart = this->current_token() - 1;
  123. const pointer rend = this->before_start();
  124. if (rstart == rend) {
  125. this->set_state(state::before_begin);
  126. return;
  127. }
  128. switch (this->State) {
  129. case state::at_end: {
  130. auto posSep = this->consume_separator(rstart, rend);
  131. if (posSep) {
  132. if (posSep == rend) {
  133. this->set_state(state::in_root_dir);
  134. } else {
  135. auto pos = this->consume_root_name(posSep, rend, true);
  136. if (pos == rend) {
  137. this->set_state(state::in_root_dir);
  138. } else {
  139. this->set_state(state::trailing_separator);
  140. }
  141. }
  142. } else {
  143. auto pos = this->consume_root_name(rstart, rend);
  144. if (pos == rend) {
  145. this->set_state(state::in_root_name);
  146. } else {
  147. this->consume_filename(rstart, rend);
  148. this->set_state(state::in_filename);
  149. }
  150. }
  151. break;
  152. }
  153. case state::trailing_separator: {
  154. this->consume_filename(rstart, rend);
  155. this->set_state(state::in_filename);
  156. break;
  157. }
  158. case state::in_filename: {
  159. auto posSep = this->consume_separator(rstart, rend);
  160. if (posSep == rend) {
  161. this->set_state(state::in_root_dir);
  162. } else {
  163. auto pos = this->consume_root_name(posSep, rend, true);
  164. if (pos == rend) {
  165. this->set_state(state::in_root_dir);
  166. } else {
  167. this->consume_filename(posSep, rend);
  168. this->set_state(state::in_filename);
  169. }
  170. }
  171. break;
  172. }
  173. case state::in_root_dir: {
  174. auto pos = this->consume_root_name(rstart, rend);
  175. if (pos) {
  176. this->set_state(state::in_root_name);
  177. }
  178. break;
  179. }
  180. case state::in_root_name:
  181. case state::before_begin: {
  182. // unreachable
  183. std::abort();
  184. }
  185. }
  186. }
  187. path_parser& operator++() noexcept
  188. {
  189. this->increment();
  190. return *this;
  191. }
  192. path_parser& operator--() noexcept
  193. {
  194. this->decrement();
  195. return *this;
  196. }
  197. cm::string_view operator*() const noexcept
  198. {
  199. switch (this->State) {
  200. case state::before_begin:
  201. case state::at_end:
  202. return cm::string_view();
  203. case state::trailing_separator:
  204. return "";
  205. case state::in_root_dir:
  206. case state::in_root_name:
  207. case state::in_filename:
  208. return this->Entry;
  209. default:
  210. // unreachable
  211. std::abort();
  212. }
  213. }
  214. void seek(seek_position position)
  215. {
  216. state s = static_cast<state>(static_cast<int>(position));
  217. while (this->State <= s) {
  218. this->increment();
  219. }
  220. }
  221. cm::string_view peek(peek_fragment fragment)
  222. {
  223. if (fragment == peek_fragment::remainder) {
  224. // peek-up remain part of the initial path
  225. return { this->Entry.data(),
  226. std::size_t(&this->Path.back() - this->Entry.data() + 1) };
  227. }
  228. if (fragment == peek_fragment::path) {
  229. // peek-up full path until current position
  230. return { this->Path.data(),
  231. std::size_t(&this->Entry.back() - this->Path.data() + 1) };
  232. }
  233. return {};
  234. }
  235. bool in_root_name() const { return this->State == state::in_root_name; }
  236. bool in_root_directory() const { return this->State == state::in_root_dir; }
  237. bool at_end() const { return this->State == state::at_end; }
  238. bool at_start() const { return this->Entry.data() == this->Path.data(); }
  239. private:
  240. void set_state(state newState) noexcept
  241. {
  242. this->State = newState;
  243. if (newState == state::before_begin || newState == state::at_end) {
  244. this->Entry = {};
  245. }
  246. }
  247. pointer before_start() const noexcept { return this->Path.data() - 1; }
  248. pointer after_end() const noexcept
  249. {
  250. return this->Path.data() + this->Path.size();
  251. }
  252. pointer current_token() const noexcept
  253. {
  254. switch (this->State) {
  255. case state::before_begin:
  256. case state::in_root_name:
  257. return &this->Path.front();
  258. case state::in_root_dir:
  259. case state::in_filename:
  260. case state::trailing_separator:
  261. return &this->Entry.front();
  262. case state::at_end:
  263. return &this->Path.back() + 1;
  264. default:
  265. // unreachable
  266. std::abort();
  267. }
  268. }
  269. pointer next_token() const noexcept
  270. {
  271. switch (this->State) {
  272. case state::before_begin:
  273. return this->Path.data();
  274. case state::in_root_name:
  275. case state::in_root_dir:
  276. case state::in_filename:
  277. return &this->Entry.back() + 1;
  278. case state::trailing_separator:
  279. case state::at_end:
  280. return after_end();
  281. default:
  282. // unreachable
  283. std::abort();
  284. }
  285. }
  286. pointer consume_separator(pointer ptr, pointer end) noexcept
  287. {
  288. if (ptr == end ||
  289. (*ptr != '/'
  290. # if defined(_WIN32)
  291. && *ptr != '\\'
  292. # endif
  293. )) {
  294. return nullptr;
  295. }
  296. const auto step = ptr < end ? 1 : -1;
  297. ptr += step;
  298. while (ptr != end &&
  299. (*ptr == '/'
  300. # if defined(_WIN32)
  301. || *ptr == ' \\'
  302. # endif
  303. )) {
  304. ptr += step;
  305. }
  306. if (step == 1) {
  307. this->Entry = cm::string_view(ptr - 1, 1);
  308. } else {
  309. this->Entry = cm::string_view(ptr + 1, 1);
  310. }
  311. return ptr;
  312. }
  313. pointer consume_filename(pointer ptr, pointer end) noexcept
  314. {
  315. auto start = ptr;
  316. if (ptr == end || *ptr == '/'
  317. # if defined(_WIN32)
  318. || *ptr == '\\'
  319. # endif
  320. ) {
  321. return nullptr;
  322. }
  323. const auto step = ptr < end ? 1 : -1;
  324. ptr += step;
  325. while (ptr != end && *ptr != '/'
  326. # if defined(_WIN32)
  327. && *ptr != '\\'
  328. # endif
  329. ) {
  330. ptr += step;
  331. }
  332. # if defined(_WIN32)
  333. if (step == -1 && (start - ptr) >= 2 && ptr == end) {
  334. // rollback drive name consumption, if any
  335. if (this->is_drive_name(ptr + 1)) {
  336. ptr += 2;
  337. }
  338. if (ptr == start) {
  339. return nullptr;
  340. }
  341. }
  342. # endif
  343. if (step == 1) {
  344. this->Entry = cm::string_view(start, ptr - start);
  345. } else {
  346. this->Entry = cm::string_view(ptr + 1, start - ptr);
  347. }
  348. return ptr;
  349. }
  350. # if defined(_WIN32)
  351. bool is_drive_name(pointer ptr)
  352. {
  353. return std::toupper(ptr[0]) >= 'A' && std::toupper(ptr[0]) <= 'Z' &&
  354. ptr[1] == ':';
  355. }
  356. # endif
  357. pointer consume_root_name(pointer ptr, pointer end,
  358. bool check_only = false) noexcept
  359. {
  360. # if defined(_WIN32)
  361. if (ptr < end) {
  362. if ((end - ptr) >= 2 && this->is_drive_name(ptr)) {
  363. // Drive letter (X:) is a root name
  364. if (!check_only) {
  365. this->Entry = cm::string_view(ptr, 2);
  366. }
  367. return ptr + 2;
  368. }
  369. if ((end - ptr) > 2 && (ptr[0] == '/' || ptr[0] == '\\') &&
  370. (ptr[1] == '/' || ptr[1] == '\\') &&
  371. (ptr[2] != '/' && ptr[2] != '\\')) {
  372. // server name (//server) is a root name
  373. auto pos = std::find(ptr + 2, end, '/');
  374. if (!check_only) {
  375. this->Entry = cm::string_view(ptr, pos - ptr);
  376. }
  377. return pos;
  378. }
  379. } else {
  380. if ((ptr - end) >= 2 && this->is_drive_name(ptr - 1)) {
  381. // Drive letter (X:) is a root name
  382. if (!check_only) {
  383. this->Entry = cm::string_view(ptr - 1, 2);
  384. }
  385. return ptr - 2;
  386. }
  387. if ((ptr - end) > 2 && (ptr[0] != '/' && ptr[0] != '\\')) {
  388. std::reverse_iterator<pointer> start(ptr);
  389. std::reverse_iterator<pointer> stop(end);
  390. auto res = std::find_if(start, stop,
  391. [](char c) { return c == '/' || c == '\\'; });
  392. pointer pos = res.base() - 1;
  393. if ((pos - 1) > end && (pos[-1] == '/' || pos[-1] == '\\')) {
  394. // server name (//server) is a root name
  395. if (!check_only) {
  396. this->Entry = cm::string_view(pos - 1, ptr - pos + 2);
  397. }
  398. return pos - 2;
  399. }
  400. }
  401. }
  402. # else
  403. (void)ptr;
  404. (void)end;
  405. (void)check_only;
  406. # endif
  407. return nullptr;
  408. }
  409. state State;
  410. const cm::string_view Path;
  411. cm::string_view Entry;
  412. };
  413. // class unicode_helper
  414. void unicode_helper::append(std::string& str, std::uint32_t codepoint)
  415. {
  416. if (codepoint <= 0x7f) {
  417. str.push_back(static_cast<char>(codepoint));
  418. } else if (codepoint >= 0x80 && codepoint <= 0x7ff) {
  419. str.push_back(static_cast<char>((codepoint >> 6) + 192));
  420. str.push_back(static_cast<char>((codepoint & 0x3f) + 128));
  421. } else if ((codepoint >= 0x800 && codepoint <= 0xd7ff) ||
  422. (codepoint >= 0xe000 && codepoint <= 0xffff)) {
  423. str.push_back(static_cast<char>((codepoint >> 12) + 224));
  424. str.push_back(static_cast<char>(((codepoint & 0xfff) >> 6) + 128));
  425. str.push_back(static_cast<char>((codepoint & 0x3f) + 128));
  426. } else if (codepoint >= 0x10000 && codepoint <= 0x10ffff) {
  427. str.push_back(static_cast<char>((codepoint >> 18) + 240));
  428. str.push_back(static_cast<char>(((codepoint & 0x3ffff) >> 12) + 128));
  429. str.push_back(static_cast<char>(((codepoint & 0xfff) >> 6) + 128));
  430. str.push_back(static_cast<char>((codepoint & 0x3f) + 128));
  431. } else {
  432. append(str, 0xfffd);
  433. }
  434. }
  435. unicode_helper::utf8_state unicode_helper::decode(const utf8_state state,
  436. const std::uint8_t fragment,
  437. std::uint32_t& codepoint)
  438. {
  439. const std::uint32_t utf8_state_info[] = {
  440. // encoded states
  441. 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u,
  442. 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u,
  443. 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu,
  444. 0x99999999u, 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u,
  445. 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u,
  446. 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u,
  447. 0u, 0u,
  448. };
  449. std::uint8_t category = fragment < 128
  450. ? 0
  451. : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf;
  452. codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu)
  453. : (0xffu >> category) & fragment);
  454. return state == s_reject
  455. ? s_reject
  456. : static_cast<utf8_state>(
  457. (utf8_state_info[category + 16] >> (state << 2)) & 0xf);
  458. }
  459. } // internals
  460. // Class path
  461. path& path::operator/=(const path& p)
  462. {
  463. if (p.is_absolute() ||
  464. (p.has_root_name() && p.get_root_name() != this->get_root_name())) {
  465. this->path_ = p.path_;
  466. return *this;
  467. }
  468. if (p.has_root_directory()) {
  469. this->path_ = static_cast<std::string>(this->get_root_name());
  470. this->path_ += static_cast<std::string>(p.get_root_directory());
  471. } else if (this->has_filename()) {
  472. this->path_ += this->preferred_separator;
  473. # if defined(_WIN32)
  474. // special case: "//host" / "b" => "//host/b"
  475. } else if (this->has_root_name() && !this->has_root_directory()) {
  476. if (this->path_.length() >= 3 &&
  477. (this->path_[0] == '/' || this->path_[0] == '\\') &&
  478. (this->path_[1] == '/' || this->path_[1] == '\\') &&
  479. (this->path_[2] != '/' || this->path_[2] != '\\')) {
  480. this->path_ += this->preferred_separator;
  481. }
  482. # endif
  483. }
  484. this->path_ += p.get_relative_path();
  485. return *this;
  486. }
  487. path path::lexically_normal() const
  488. {
  489. if (this->path_.empty()) {
  490. return *this;
  491. }
  492. const cm::string_view dot = "."_s;
  493. const cm::string_view dotdot = ".."_s;
  494. std::vector<cm::string_view> root_parts;
  495. std::vector<cm::string_view> parts;
  496. bool root_directory_defined = false;
  497. bool need_final_separator = false;
  498. std::size_t path_size = 0;
  499. internals::path_parser parser(this->path_);
  500. ++parser;
  501. while (!parser.at_end()) {
  502. auto part = *parser;
  503. if (parser.in_root_name() || parser.in_root_directory()) {
  504. if (parser.in_root_directory()) {
  505. root_directory_defined = true;
  506. }
  507. root_parts.push_back(part);
  508. path_size += part.size();
  509. } else if (part == dotdot) {
  510. if (!parts.empty() && parts.back() != dotdot) {
  511. need_final_separator = true;
  512. path_size -= parts.back().size();
  513. parts.pop_back();
  514. } else if ((parts.empty() || parts.back() == dotdot) &&
  515. !root_directory_defined) {
  516. parts.push_back(dotdot);
  517. path_size += 2;
  518. }
  519. } else if (part == dot || part.empty()) {
  520. need_final_separator = true;
  521. if (part.empty()) {
  522. parts.push_back(part);
  523. }
  524. } else {
  525. // filename
  526. need_final_separator = false;
  527. parts.push_back(part);
  528. path_size += part.size();
  529. }
  530. ++parser;
  531. }
  532. // no final separator if last element of path is ".."
  533. need_final_separator =
  534. need_final_separator && !parts.empty() && parts.back() != dotdot;
  535. // build final path
  536. //// compute final size of path
  537. path_size += parts.size() + (need_final_separator ? 1 : 0);
  538. std::string np;
  539. np.reserve(path_size);
  540. for (const auto& p : root_parts) {
  541. np += p;
  542. }
  543. // convert any slash to the preferred_separator
  544. if (static_cast<std::string::value_type>(this->preferred_separator) != '/') {
  545. std::replace(
  546. np.begin(), np.end(), '/',
  547. static_cast<std::string::value_type>(this->preferred_separator));
  548. }
  549. for (const auto& p : parts) {
  550. if (!p.empty()) {
  551. np += p;
  552. np += static_cast<std::string::value_type>(this->preferred_separator);
  553. }
  554. }
  555. if (!parts.empty() && !need_final_separator) {
  556. // remove extra separator
  557. np.pop_back();
  558. }
  559. if (np.empty()) {
  560. np.assign(1, '.');
  561. }
  562. return path(std::move(np));
  563. }
  564. path path::lexically_relative(const path& base) const
  565. {
  566. internals::path_parser parser(this->path_);
  567. ++parser;
  568. internals::path_parser parserbase(base.path_);
  569. ++parserbase;
  570. cm::string_view this_root_name, base_root_name;
  571. cm::string_view this_root_dir, base_root_dir;
  572. if (parser.in_root_name()) {
  573. this_root_name = *parser;
  574. ++parser;
  575. }
  576. if (parser.in_root_directory()) {
  577. this_root_dir = *parser;
  578. ++parser;
  579. }
  580. if (parserbase.in_root_name()) {
  581. base_root_name = *parserbase;
  582. ++parserbase;
  583. }
  584. if (parserbase.in_root_directory()) {
  585. base_root_dir = *parserbase;
  586. ++parserbase;
  587. }
  588. auto is_path_absolute = [](cm::string_view rn, cm::string_view rd) -> bool {
  589. # if defined(_WIN32)
  590. return !rn.empty() && !rd.empty();
  591. # else
  592. (void)rn;
  593. return !rd.empty();
  594. # endif
  595. };
  596. if (this_root_name != base_root_name ||
  597. is_path_absolute(this_root_name, this_root_dir) !=
  598. is_path_absolute(base_root_name, base_root_dir) ||
  599. (this_root_dir.empty() && !base_root_dir.empty())) {
  600. return path();
  601. }
  602. # if defined(_WIN32)
  603. // LWG3070 handle special case: filename can also be a root-name
  604. auto is_drive_name = [](cm::string_view item) -> bool {
  605. return item.length() == 2 && item[1] == ':';
  606. };
  607. parser.reset();
  608. parser.seek(internals::path_parser::seek_position::root_directory);
  609. while (!parser.at_end()) {
  610. if (is_drive_name(*parser)) {
  611. return path();
  612. }
  613. ++parser;
  614. }
  615. parserbase.reset();
  616. parserbase.seek(internals::path_parser::seek_position::root_directory);
  617. while (!parserbase.at_end()) {
  618. if (is_drive_name(*parserbase)) {
  619. return path();
  620. }
  621. ++parserbase;
  622. }
  623. # endif
  624. const cm::string_view dot = "."_s;
  625. const cm::string_view dotdot = ".."_s;
  626. auto a = this->begin(), aend = this->end();
  627. auto b = base.begin(), bend = base.end();
  628. while (a != aend && b != bend && a->string() == b->string()) {
  629. ++a;
  630. ++b;
  631. }
  632. int count = 0;
  633. for (; b != bend; ++b) {
  634. auto part = *b;
  635. if (part == dotdot) {
  636. --count;
  637. } else if (part.string() != dot && !part.empty()) {
  638. ++count;
  639. }
  640. }
  641. if (count == 0 && (a == this->end() || a->empty())) {
  642. return path(dot);
  643. }
  644. if (count >= 0) {
  645. path result;
  646. path p_dotdot(dotdot);
  647. for (int i = 0; i < count; ++i) {
  648. result /= p_dotdot;
  649. }
  650. for (; a != aend; ++a) {
  651. result /= *a;
  652. }
  653. return result;
  654. }
  655. // count < 0
  656. return path();
  657. }
  658. path::path_type path::get_generic() const
  659. {
  660. auto gen_path = this->path_;
  661. auto start = gen_path.begin();
  662. # if defined(_WIN32)
  663. std::replace(gen_path.begin(), gen_path.end(), '\\', '/');
  664. // preserve special syntax for root_name ('//server' or '//?')
  665. if (gen_path.length() > 2 && gen_path[2] != '/') {
  666. start += 2;
  667. }
  668. # endif
  669. // remove duplicate separators
  670. auto new_end = std::unique(start, gen_path.end(), [](char lhs, char rhs) {
  671. return lhs == rhs && lhs == '/';
  672. });
  673. gen_path.erase(new_end, gen_path.end());
  674. return gen_path;
  675. }
  676. cm::string_view path::get_root_name() const
  677. {
  678. internals::path_parser parser(this->path_);
  679. ++parser;
  680. if (parser.in_root_name()) {
  681. return *parser;
  682. }
  683. return {};
  684. }
  685. cm::string_view path::get_root_directory() const
  686. {
  687. internals::path_parser parser(this->path_);
  688. ++parser;
  689. if (parser.in_root_name()) {
  690. ++parser;
  691. }
  692. if (parser.in_root_directory()) {
  693. return *parser;
  694. }
  695. return {};
  696. }
  697. cm::string_view path::get_relative_path() const
  698. {
  699. internals::path_parser parser(this->path_);
  700. parser.seek(internals::path_parser::seek_position::root_directory);
  701. if (parser.at_end()) {
  702. return {};
  703. }
  704. return parser.peek(internals::path_parser::peek_fragment::remainder);
  705. }
  706. cm::string_view path::get_parent_path() const
  707. {
  708. if (!this->has_relative_path()) {
  709. return this->path_;
  710. }
  711. // peek-up full path minus last element
  712. internals::path_parser parser(this->path_, true);
  713. --parser;
  714. if (parser.at_start()) {
  715. return {};
  716. }
  717. --parser;
  718. return parser.peek(internals::path_parser::peek_fragment::path);
  719. }
  720. cm::string_view path::get_filename() const
  721. {
  722. {
  723. internals::path_parser parser(this->path_);
  724. parser.seek(internals::path_parser::seek_position::root_directory);
  725. if (parser.at_end()) {
  726. return {};
  727. }
  728. }
  729. {
  730. internals::path_parser parser(this->path_, true);
  731. return *(--parser);
  732. }
  733. }
  734. cm::string_view path::get_filename_fragment(filename_fragment fragment) const
  735. {
  736. auto file = this->get_filename();
  737. if (file == "." || file == ".." || file.empty()) {
  738. return fragment == filename_fragment::stem ? file : cm::string_view{};
  739. }
  740. auto pos = file.find_last_of('.');
  741. if (pos == cm::string_view::npos || pos == 0) {
  742. return fragment == filename_fragment::stem ? file : cm::string_view{};
  743. }
  744. return fragment == filename_fragment::stem ? file.substr(0, pos)
  745. : file.substr(pos);
  746. }
  747. int path::compare_path(cm::string_view str) const
  748. {
  749. internals::path_parser this_pp(this->path_);
  750. ++this_pp;
  751. internals::path_parser other_pp(str);
  752. ++other_pp;
  753. // compare root_name part
  754. {
  755. bool compare_root_names = false;
  756. cm::string_view this_root_name, other_root_name;
  757. int res;
  758. if (this_pp.in_root_name()) {
  759. compare_root_names = true;
  760. this_root_name = *this_pp;
  761. ++this_pp;
  762. }
  763. if (other_pp.in_root_name()) {
  764. compare_root_names = true;
  765. other_root_name = *other_pp;
  766. ++other_pp;
  767. }
  768. if (compare_root_names &&
  769. (res = this_root_name.compare(other_root_name) != 0)) {
  770. return res;
  771. }
  772. }
  773. // compare root_directory part
  774. {
  775. if (!this_pp.in_root_directory() && other_pp.in_root_directory()) {
  776. return -1;
  777. } else if (this_pp.in_root_directory() && !other_pp.in_root_directory()) {
  778. return 1;
  779. }
  780. if (this_pp.in_root_directory()) {
  781. ++this_pp;
  782. }
  783. if (other_pp.in_root_directory()) {
  784. ++other_pp;
  785. }
  786. }
  787. // compare various parts of the paths
  788. while (!this_pp.at_end() && !other_pp.at_end()) {
  789. int res;
  790. if ((res = (*this_pp).compare(*other_pp)) != 0) {
  791. return res;
  792. }
  793. ++this_pp;
  794. ++other_pp;
  795. }
  796. // final step
  797. if (this_pp.at_end() && !other_pp.at_end()) {
  798. return -1;
  799. } else if (!this_pp.at_end() && other_pp.at_end()) {
  800. return 1;
  801. }
  802. return 0;
  803. }
  804. // Class path::iterator
  805. path::iterator::iterator()
  806. : path_(nullptr)
  807. {
  808. }
  809. path::iterator::iterator(const iterator& other)
  810. {
  811. this->path_ = other.path_;
  812. if (other.parser_) {
  813. this->parser_ = cm::make_unique<internals::path_parser>(*other.parser_);
  814. this->path_element_ = path(**this->parser_);
  815. }
  816. }
  817. path::iterator::iterator(const path* p, bool at_end)
  818. : path_(p)
  819. , parser_(cm::make_unique<internals::path_parser>(p->path_, at_end))
  820. {
  821. if (!at_end) {
  822. ++(*this->parser_);
  823. this->path_element_ = path(**this->parser_);
  824. }
  825. }
  826. path::iterator::~iterator() = default;
  827. path::iterator& path::iterator::operator=(const iterator& other)
  828. {
  829. this->path_ = other.path_;
  830. if (other.parser_) {
  831. this->parser_ = cm::make_unique<internals::path_parser>(*other.parser_);
  832. this->path_element_ = path(**this->parser_);
  833. }
  834. return *this;
  835. }
  836. path::iterator& path::iterator::operator++()
  837. {
  838. assert(this->parser_);
  839. if (this->parser_) {
  840. assert(!this->parser_->at_end());
  841. if (!this->parser_->at_end()) {
  842. ++(*this->parser_);
  843. if (this->parser_->at_end()) {
  844. this->path_element_ = path();
  845. } else {
  846. this->path_element_ = path(**this->parser_);
  847. }
  848. }
  849. }
  850. return *this;
  851. }
  852. path::iterator& path::iterator::operator--()
  853. {
  854. assert(this->parser_);
  855. if (this->parser_) {
  856. assert(!this->parser_->at_start());
  857. if (!this->parser_->at_start()) {
  858. --(*this->parser_);
  859. this->path_element_ = path(**this->parser_);
  860. }
  861. }
  862. return *this;
  863. }
  864. bool operator==(const path::iterator& lhs, const path::iterator& rhs)
  865. {
  866. return lhs.path_ == rhs.path_ && lhs.parser_ != nullptr &&
  867. ((lhs.parser_->at_end() && rhs.parser_->at_end()) ||
  868. (lhs.parser_->at_start() && rhs.parser_->at_start()) ||
  869. ((**lhs.parser_).data() == (**rhs.parser_).data()));
  870. }
  871. std::size_t hash_value(const path& p) noexcept
  872. {
  873. internals::path_parser parser(p.path_);
  874. std::hash<cm::string_view> hasher;
  875. std::size_t value = 0;
  876. while (!parser.at_end()) {
  877. value = hasher(*parser) + 0x9e3779b9 + (value << 6) + (value >> 2);
  878. ++parser;
  879. }
  880. return value;
  881. }
  882. } // filesystem
  883. } // cm
  884. #else
  885. // Avoid empty translation unit.
  886. void cm_filesystem_path_cxx()
  887. {
  888. }
  889. #endif