fs_path.cxx 25 KB

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