1
0

fs_path.cxx 26 KB

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