BsPath.cpp 12 KB

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