BsPath.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include <Debug/BsDebug.h>
  4. #include "Prerequisites/BsPrerequisitesUtil.h"
  5. #include "Error/BsException.h"
  6. #include "String/BsUnicode.h"
  7. namespace bs
  8. {
  9. const Path Path::BLANK = Path();
  10. Path::Path()
  11. :mIsAbsolute(false)
  12. { }
  13. Path::Path(const WString& pathStr, PathType type)
  14. {
  15. assign(pathStr, type);
  16. }
  17. Path::Path(const String& pathStr, PathType type)
  18. {
  19. assign(pathStr, type);
  20. }
  21. Path::Path(const wchar_t* pathStr, PathType type)
  22. {
  23. assign(pathStr);
  24. }
  25. Path::Path(const char* pathStr, PathType type)
  26. {
  27. assign(pathStr);
  28. }
  29. Path::Path(const Path& other)
  30. {
  31. assign(other);
  32. }
  33. Path& Path::operator= (const Path& path)
  34. {
  35. assign(path);
  36. return *this;
  37. }
  38. Path& Path::operator= (const WString& pathStr)
  39. {
  40. assign(pathStr);
  41. return *this;
  42. }
  43. Path& Path::operator= (const String& pathStr)
  44. {
  45. assign(pathStr);
  46. return *this;
  47. }
  48. Path& Path::operator= (const wchar_t* pathStr)
  49. {
  50. assign(pathStr);
  51. return *this;
  52. }
  53. Path& Path::operator= (const char* pathStr)
  54. {
  55. assign(pathStr);
  56. return *this;
  57. }
  58. void Path::swap(Path& path)
  59. {
  60. std::swap(mDirectories, path.mDirectories);
  61. std::swap(mFilename, path.mFilename);
  62. std::swap(mDevice, path.mDevice);
  63. std::swap(mNode, path.mNode);
  64. std::swap(mIsAbsolute, path.mIsAbsolute);
  65. }
  66. void Path::assign(const Path& path)
  67. {
  68. mDirectories = path.mDirectories;
  69. mFilename = path.mFilename;
  70. mDevice = path.mDevice;
  71. mNode = path.mNode;
  72. mIsAbsolute = path.mIsAbsolute;
  73. }
  74. void Path::assign(const WString& pathStr, PathType type)
  75. {
  76. assign(pathStr.data(), (UINT32)pathStr.length(), type);
  77. }
  78. void Path::assign(const String& pathStr, PathType type)
  79. {
  80. assign(pathStr.data(), (UINT32)pathStr.length(), type);
  81. }
  82. void Path::assign(const wchar_t* pathStr, PathType type)
  83. {
  84. assign(pathStr, (UINT32)wcslen(pathStr), type);
  85. }
  86. void Path::assign(const char* pathStr, PathType type)
  87. {
  88. assign(pathStr, (UINT32)strlen(pathStr), type);
  89. }
  90. void Path::assign(const wchar_t* pathStr, UINT32 numChars, PathType type)
  91. {
  92. switch (type)
  93. {
  94. case PathType::Windows:
  95. parseWindows(pathStr, numChars);
  96. break;
  97. case PathType::Unix:
  98. parseUnix(pathStr, numChars);
  99. break;
  100. default:
  101. #if BS_PLATFORM == BS_PLATFORM_WIN32
  102. parseWindows(pathStr, numChars);
  103. #elif BS_PLATFORM == BS_PLATFORM_OSX || BS_PLATFORM == BS_PLATFORM_LINUX
  104. parseUnix(pathStr, numChars);
  105. #else
  106. static_assert(false, "Unsupported platform for path.");
  107. #endif
  108. break;
  109. }
  110. }
  111. void Path::assign(const char* pathStr, UINT32 numChars, PathType type)
  112. {
  113. switch (type)
  114. {
  115. case PathType::Windows:
  116. parseWindows(pathStr, numChars);
  117. break;
  118. case PathType::Unix:
  119. parseUnix(pathStr, numChars);
  120. break;
  121. default:
  122. #if BS_PLATFORM == BS_PLATFORM_WIN32
  123. parseWindows(pathStr, numChars);
  124. #elif BS_PLATFORM == BS_PLATFORM_OSX || BS_PLATFORM == BS_PLATFORM_LINUX
  125. parseUnix(pathStr, numChars);
  126. #else
  127. static_assert(false, "Unsupported platform for path.");
  128. #endif
  129. break;
  130. }
  131. }
  132. WString Path::toWString(PathType type) const
  133. {
  134. switch (type)
  135. {
  136. case PathType::Windows:
  137. return UTF8::toWide(buildWindows());
  138. case PathType::Unix:
  139. return UTF8::toWide(buildUnix());
  140. default:
  141. #if BS_PLATFORM == BS_PLATFORM_WIN32
  142. return UTF8::toWide(buildWindows());
  143. #elif BS_PLATFORM == BS_PLATFORM_OSX || BS_PLATFORM == BS_PLATFORM_LINUX
  144. return UTF8::toWide(buildUnix());
  145. #else
  146. static_assert(false, "Unsupported platform for path.");
  147. #endif
  148. break;
  149. }
  150. }
  151. String Path::toString(PathType type) const
  152. {
  153. switch (type)
  154. {
  155. case PathType::Windows:
  156. return buildWindows();
  157. case PathType::Unix:
  158. return buildUnix();
  159. default:
  160. #if BS_PLATFORM == BS_PLATFORM_WIN32
  161. return buildWindows();
  162. #elif BS_PLATFORM == BS_PLATFORM_OSX || BS_PLATFORM == BS_PLATFORM_LINUX
  163. return buildUnix();
  164. #else
  165. static_assert(false, "Unsupported platform for path.");
  166. #endif
  167. break;
  168. }
  169. }
  170. Path Path::getParent() const
  171. {
  172. Path copy = *this;
  173. copy.makeParent();
  174. return copy;
  175. }
  176. Path Path::getAbsolute(const Path& base) const
  177. {
  178. Path copy = *this;
  179. copy.makeAbsolute(base);
  180. return copy;
  181. }
  182. Path Path::getRelative(const Path& base) const
  183. {
  184. Path copy = *this;
  185. copy.makeRelative(base);
  186. return copy;
  187. }
  188. Path Path::getDirectory() const
  189. {
  190. Path copy = *this;
  191. copy.mFilename.clear();
  192. return copy;
  193. }
  194. Path& Path::makeParent()
  195. {
  196. if (mFilename.empty())
  197. {
  198. if (mDirectories.empty())
  199. {
  200. if (!mIsAbsolute)
  201. mDirectories.push_back("..");
  202. }
  203. else
  204. {
  205. if (mDirectories.back() == "..")
  206. mDirectories.push_back("..");
  207. else
  208. mDirectories.pop_back();
  209. }
  210. }
  211. else
  212. {
  213. mFilename.clear();
  214. }
  215. return *this;
  216. }
  217. Path& Path::makeAbsolute(const Path& base)
  218. {
  219. if (mIsAbsolute)
  220. return *this;
  221. Path absDir = base.getDirectory();
  222. if (base.isFile())
  223. absDir.pushDirectory(base.mFilename);
  224. for (auto& dir : mDirectories)
  225. absDir.pushDirectory(dir);
  226. absDir.setFilename(mFilename);
  227. *this = absDir;
  228. return *this;
  229. }
  230. Path& Path::makeRelative(const Path& base)
  231. {
  232. if (!base.includes(*this))
  233. return *this;
  234. mDirectories.erase(mDirectories.begin(), mDirectories.begin() + base.mDirectories.size());
  235. // Sometimes a directory name can be interpreted as a file and we're okay with that. Check for that
  236. // special case.
  237. if (base.isFile())
  238. {
  239. if (mDirectories.size() > 0)
  240. mDirectories.erase(mDirectories.begin());
  241. else
  242. mFilename = "";
  243. }
  244. mDevice = "";
  245. mNode = "";
  246. mIsAbsolute = false;
  247. return *this;
  248. }
  249. bool Path::includes(const Path& child) const
  250. {
  251. if (mDevice != child.mDevice)
  252. return false;
  253. if (mNode != child.mNode)
  254. return false;
  255. auto iterParent = mDirectories.begin();
  256. auto iterChild = child.mDirectories.begin();
  257. for (; iterParent != mDirectories.end(); ++iterChild, ++iterParent)
  258. {
  259. if (iterChild == child.mDirectories.end())
  260. return false;
  261. if (!comparePathElem(*iterChild, *iterParent))
  262. return false;
  263. }
  264. if (!mFilename.empty())
  265. {
  266. if (iterChild == child.mDirectories.end())
  267. {
  268. if (child.mFilename.empty())
  269. return false;
  270. if (!comparePathElem(child.mFilename, mFilename))
  271. return false;
  272. }
  273. else
  274. {
  275. if (!comparePathElem(*iterChild, mFilename))
  276. return false;
  277. }
  278. }
  279. return true;
  280. }
  281. bool Path::equals(const Path& other) const
  282. {
  283. if (mIsAbsolute != other.mIsAbsolute)
  284. return false;
  285. if (mIsAbsolute)
  286. {
  287. if (!comparePathElem(mDevice, other.mDevice))
  288. return false;
  289. }
  290. if (!comparePathElem(mNode, other.mNode))
  291. return false;
  292. UINT32 myNumElements = (UINT32)mDirectories.size();
  293. UINT32 otherNumElements = (UINT32)other.mDirectories.size();
  294. if (!mFilename.empty())
  295. myNumElements++;
  296. if (!other.mFilename.empty())
  297. otherNumElements++;
  298. if (myNumElements != otherNumElements)
  299. return false;
  300. if(myNumElements > 0)
  301. {
  302. auto iterMe = mDirectories.begin();
  303. auto iterOther = other.mDirectories.begin();
  304. for(UINT32 i = 0; i < (myNumElements - 1); i++, ++iterMe, ++iterOther)
  305. {
  306. if (!comparePathElem(*iterMe, *iterOther))
  307. return false;
  308. }
  309. if (!mFilename.empty())
  310. {
  311. if (!other.mFilename.empty())
  312. {
  313. if (!comparePathElem(mFilename, other.mFilename))
  314. return false;
  315. }
  316. else
  317. {
  318. if (!comparePathElem(mFilename, *iterOther))
  319. return false;
  320. }
  321. }
  322. else
  323. {
  324. if (!other.mFilename.empty())
  325. {
  326. if (!comparePathElem(*iterMe, other.mFilename))
  327. return false;
  328. }
  329. else
  330. {
  331. if (!comparePathElem(*iterMe, *iterOther))
  332. return false;
  333. }
  334. }
  335. }
  336. return true;
  337. }
  338. Path& Path::append(const Path& path)
  339. {
  340. if (!mFilename.empty())
  341. pushDirectory(mFilename);
  342. for (auto& dir : path.mDirectories)
  343. pushDirectory(dir);
  344. mFilename = path.mFilename;
  345. return *this;
  346. }
  347. void Path::setFilename(const WString& filename)
  348. {
  349. mFilename = UTF8::fromWide(filename);
  350. }
  351. void Path::setBasename(const WString& basename)
  352. {
  353. mFilename = UTF8::fromWide(basename) + getExtension();
  354. }
  355. void Path::setBasename(const String& basename)
  356. {
  357. mFilename = basename + getExtension();
  358. }
  359. void Path::setExtension(const WString& extension)
  360. {
  361. setExtension(UTF8::fromWide(extension));
  362. }
  363. void Path::setExtension(const String& extension)
  364. {
  365. StringStream stream;
  366. stream << getFilename(false);
  367. stream << extension;
  368. mFilename = stream.str();
  369. }
  370. WString Path::getWFilename(bool extension) const
  371. {
  372. return UTF8::toWide(getFilename(extension));
  373. }
  374. String Path::getFilename(bool extension) const
  375. {
  376. if (extension)
  377. return mFilename;
  378. else
  379. {
  380. WString::size_type pos = mFilename.rfind(L'.');
  381. if (pos != WString::npos)
  382. return mFilename.substr(0, pos);
  383. else
  384. return mFilename;
  385. }
  386. }
  387. WString Path::getWExtension() const
  388. {
  389. return UTF8::toWide(getExtension());
  390. }
  391. String Path::getExtension() const
  392. {
  393. WString::size_type pos = mFilename.rfind(L'.');
  394. if (pos != WString::npos)
  395. return mFilename.substr(pos);
  396. else
  397. return String();
  398. }
  399. WString Path::getWDirectory(UINT32 idx) const
  400. {
  401. return UTF8::toWide(getDirectory(idx));
  402. }
  403. const String& Path::getDirectory(UINT32 idx) const
  404. {
  405. if (idx >= (UINT32)mDirectories.size())
  406. {
  407. BS_EXCEPT(InvalidParametersException, "Index out of range: " + bs::toString(idx) + ". Valid range: [0, " +
  408. bs::toString((UINT32)mDirectories.size() - 1) + "]");
  409. }
  410. return mDirectories[idx];
  411. }
  412. WString Path::getWDevice() const
  413. {
  414. return UTF8::toWide(mDevice);
  415. }
  416. WString Path::getWNode() const
  417. {
  418. return UTF8::toWide(mNode);
  419. }
  420. const String& Path::getTail() const
  421. {
  422. if (isFile())
  423. return mFilename;
  424. else if (mDirectories.size() > 0)
  425. return mDirectories.back();
  426. else
  427. return StringUtil::BLANK;
  428. }
  429. WString Path::getWTail() const
  430. {
  431. return UTF8::toWide(getTail());
  432. }
  433. void Path::clear()
  434. {
  435. mDirectories.clear();
  436. mDevice.clear();
  437. mFilename.clear();
  438. mNode.clear();
  439. mIsAbsolute = false;
  440. }
  441. void Path::throwInvalidPathException(const WString& path) const
  442. {
  443. BS_EXCEPT(InvalidParametersException, "Incorrectly formatted path provided: " + bs::toString(path));
  444. }
  445. void Path::throwInvalidPathException(const String& path) const
  446. {
  447. BS_EXCEPT(InvalidParametersException, "Incorrectly formatted path provided: " + path);
  448. }
  449. void Path::setNode(const WString& node)
  450. {
  451. mNode = UTF8::fromWide(node);
  452. }
  453. void Path::setDevice(const WString& device)
  454. {
  455. mDevice = UTF8::fromWide(device);
  456. }
  457. String Path::buildWindows() const
  458. {
  459. StringStream result;
  460. if (!mNode.empty())
  461. {
  462. result << "\\\\";
  463. result << mNode;
  464. result << "\\";
  465. }
  466. else if (!mDevice.empty())
  467. {
  468. result << mDevice;
  469. result << ":\\";
  470. }
  471. else if (mIsAbsolute)
  472. {
  473. result << "\\";
  474. }
  475. for (auto& dir : mDirectories)
  476. {
  477. result << dir;
  478. result << "\\";
  479. }
  480. result << mFilename;
  481. return result.str();
  482. }
  483. String Path::buildUnix() const
  484. {
  485. StringStream result;
  486. auto dirIter = mDirectories.begin();
  487. if (!mDevice.empty())
  488. {
  489. result << "/";
  490. result << mDevice;
  491. result << ":/";
  492. }
  493. else if (mIsAbsolute)
  494. {
  495. if (dirIter != mDirectories.end() && *dirIter == "~")
  496. {
  497. result << "~";
  498. dirIter++;
  499. }
  500. result << "/";
  501. }
  502. for (; dirIter != mDirectories.end(); ++dirIter)
  503. {
  504. result << *dirIter;
  505. result << "/";
  506. }
  507. result << mFilename;
  508. return result.str();
  509. }
  510. Path Path::operator+ (const Path& rhs) const
  511. {
  512. return Path::combine(*this, rhs);
  513. }
  514. Path& Path::operator+= (const Path& rhs)
  515. {
  516. return append(rhs);
  517. }
  518. bool Path::comparePathElem(const String& left, const String& right)
  519. {
  520. if (left.size() != right.size())
  521. return false;
  522. // TODO: Case sensitive/insensitive file path actually depends on used file-system but I'm not gonna check that
  523. for (UINT32 i = 0; i < (UINT32)left.size(); i++)
  524. {
  525. if (tolower(left[i]) != tolower(right[i]))
  526. return false;
  527. }
  528. return true;
  529. }
  530. Path Path::combine(const Path& left, const Path& right)
  531. {
  532. Path output = left;
  533. return output.append(right);
  534. }
  535. void Path::pushDirectory(const WString& dir)
  536. {
  537. pushDirectory(UTF8::fromWide(dir));
  538. }
  539. void Path::pushDirectory(const String& dir)
  540. {
  541. if (!dir.empty() && dir != ".")
  542. {
  543. if (dir == "..")
  544. {
  545. if (!mDirectories.empty() && mDirectories.back() != "..")
  546. mDirectories.pop_back();
  547. else
  548. mDirectories.push_back(dir);
  549. }
  550. else
  551. mDirectories.push_back(dir);
  552. }
  553. }
  554. }