fs_path.cxx 26 KB

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