2
0

BsPath.cpp 12 KB

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