1
0

mptPathString.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  1. /*
  2. * mptPathString.cpp
  3. * -----------------
  4. * Purpose: Wrapper class around the platform-native representation of path names. Should be the only type that is used to store path names.
  5. * Notes : Currently none.
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "mptPathString.h"
  11. #include "mpt/uuid/uuid.hpp"
  12. #include "misc_util.h"
  13. #include "mptRandom.h"
  14. #if MPT_OS_WINDOWS
  15. #include <windows.h>
  16. #if defined(MODPLUG_TRACKER)
  17. #include <shlwapi.h>
  18. #endif
  19. #include <tchar.h>
  20. #endif
  21. OPENMPT_NAMESPACE_BEGIN
  22. #if MPT_OS_WINDOWS
  23. namespace mpt
  24. {
  25. RawPathString PathString::AsNativePrefixed() const
  26. {
  27. #if MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00)
  28. // For WinRT on Windows 8, there is no official wy to determine an absolute path.
  29. return path;
  30. #else
  31. if(path.length() < MAX_PATH || path.substr(0, 4) == PL_("\\\\?\\"))
  32. {
  33. // Path is short enough or already in prefixed form
  34. return path;
  35. }
  36. const RawPathString absPath = mpt::GetAbsolutePath(*this).AsNative();
  37. if(absPath.substr(0, 2) == PL_("\\\\"))
  38. {
  39. // Path is a network share: \\server\foo.bar -> \\?\UNC\server\foo.bar
  40. return PL_("\\\\?\\UNC") + absPath.substr(1);
  41. } else
  42. {
  43. // Regular file: C:\foo.bar -> \\?\C:\foo.bar
  44. return PL_("\\\\?\\") + absPath;
  45. }
  46. #endif
  47. }
  48. #if !MPT_OS_WINDOWS_WINRT
  49. int PathString::CompareNoCase(const PathString & a, const PathString & b)
  50. {
  51. return lstrcmpi(a.path.c_str(), b.path.c_str());
  52. }
  53. #endif // !MPT_OS_WINDOWS_WINRT
  54. // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries
  55. // Note: We use our own implementation as PathCanonicalize is limited to MAX_PATH
  56. // and unlimited versions are only available on Windows 8 and later.
  57. // Furthermore, we also convert forward-slashes to backslashes and always remove trailing slashes.
  58. PathString PathString::Simplify() const
  59. {
  60. if(path.empty())
  61. return PathString();
  62. std::vector<RawPathString> components;
  63. RawPathString root;
  64. RawPathString::size_type startPos = 0;
  65. if(path.size() >= 2 && path[1] == PC_(':'))
  66. {
  67. // Drive letter
  68. root = path.substr(0, 2) + PC_('\\');
  69. startPos = 2;
  70. } else if(path.substr(0, 2) == PL_("\\\\"))
  71. {
  72. // Network share
  73. root = PL_("\\\\");
  74. startPos = 2;
  75. } else if(path.substr(0, 2) == PL_(".\\") || path.substr(0, 2) == PL_("./"))
  76. {
  77. // Special case for relative paths
  78. root = PL_(".\\");
  79. startPos = 2;
  80. } else if(path.size() >= 1 && (path[0] == PC_('\\') || path[0] == PC_('/')))
  81. {
  82. // Special case for relative paths
  83. root = PL_("\\");
  84. startPos = 1;
  85. }
  86. while(startPos < path.size())
  87. {
  88. auto pos = path.find_first_of(PL_("\\/"), startPos);
  89. if(pos == RawPathString::npos)
  90. pos = path.size();
  91. mpt::RawPathString dir = path.substr(startPos, pos - startPos);
  92. if(dir == PL_(".."))
  93. {
  94. // Go back one directory
  95. if(!components.empty())
  96. {
  97. components.pop_back();
  98. }
  99. } else if(dir == PL_("."))
  100. {
  101. // nop
  102. } else if(!dir.empty())
  103. {
  104. components.push_back(std::move(dir));
  105. }
  106. startPos = pos + 1;
  107. }
  108. RawPathString result = root;
  109. result.reserve(path.size());
  110. for(const auto &component : components)
  111. {
  112. result += component + PL_("\\");
  113. }
  114. if(!components.empty())
  115. result.pop_back();
  116. return mpt::PathString(result);
  117. }
  118. } // namespace mpt
  119. #endif // MPT_OS_WINDOWS
  120. namespace mpt
  121. {
  122. #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
  123. void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const
  124. {
  125. // We cannot use CRT splitpath here, because:
  126. // * limited to _MAX_PATH or similar
  127. // * no support for UNC paths
  128. // * no support for \\?\ prefixed paths
  129. if(drive) *drive = mpt::PathString();
  130. if(dir) *dir = mpt::PathString();
  131. if(fname) *fname = mpt::PathString();
  132. if(ext) *ext = mpt::PathString();
  133. mpt::RawPathString p = path;
  134. // remove \\?\\ prefix
  135. if(p.substr(0, 8) == PL_("\\\\?\\UNC\\"))
  136. {
  137. p = PL_("\\\\") + p.substr(8);
  138. } else if(p.substr(0, 4) == PL_("\\\\?\\"))
  139. {
  140. p = p.substr(4);
  141. }
  142. if (p.length() >= 2 && (
  143. p.substr(0, 2) == PL_("\\\\")
  144. || p.substr(0, 2) == PL_("\\/")
  145. || p.substr(0, 2) == PL_("/\\")
  146. || p.substr(0, 2) == PL_("//")
  147. ))
  148. { // UNC
  149. mpt::RawPathString::size_type first_slash = p.substr(2).find_first_of(PL_("\\/"));
  150. if(first_slash != mpt::RawPathString::npos)
  151. {
  152. mpt::RawPathString::size_type second_slash = p.substr(2 + first_slash + 1).find_first_of(PL_("\\/"));
  153. if(second_slash != mpt::RawPathString::npos)
  154. {
  155. if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2 + first_slash + 1 + second_slash));
  156. p = p.substr(2 + first_slash + 1 + second_slash);
  157. } else
  158. {
  159. if(drive) *drive = mpt::PathString::FromNative(p);
  160. p = mpt::RawPathString();
  161. }
  162. } else
  163. {
  164. if(drive) *drive = mpt::PathString::FromNative(p);
  165. p = mpt::RawPathString();
  166. }
  167. } else
  168. { // local
  169. if(p.length() >= 2 && (p[1] == PC_(':')))
  170. {
  171. if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2));
  172. p = p.substr(2);
  173. } else
  174. {
  175. if(drive) *drive = mpt::PathString();
  176. }
  177. }
  178. mpt::RawPathString::size_type last_slash = p.find_last_of(PL_("\\/"));
  179. if(last_slash != mpt::RawPathString::npos)
  180. {
  181. if(dir) *dir = mpt::PathString::FromNative(p.substr(0, last_slash + 1));
  182. p = p.substr(last_slash + 1);
  183. } else
  184. {
  185. if(dir) *dir = mpt::PathString();
  186. }
  187. mpt::RawPathString::size_type last_dot = p.find_last_of(PL_("."));
  188. if(last_dot == mpt::RawPathString::npos)
  189. {
  190. if(fname) *fname = mpt::PathString::FromNative(p);
  191. if(ext) *ext = mpt::PathString();
  192. } else if(last_dot == 0)
  193. {
  194. if(fname) *fname = mpt::PathString::FromNative(p);
  195. if(ext) *ext = mpt::PathString();
  196. } else if(p == PL_(".") || p == PL_(".."))
  197. {
  198. if(fname) *fname = mpt::PathString::FromNative(p);
  199. if(ext) *ext = mpt::PathString();
  200. } else
  201. {
  202. if(fname) *fname = mpt::PathString::FromNative(p.substr(0, last_dot));
  203. if(ext) *ext = mpt::PathString::FromNative(p.substr(last_dot));
  204. }
  205. }
  206. PathString PathString::GetDrive() const
  207. {
  208. PathString drive;
  209. SplitPath(&drive, nullptr, nullptr, nullptr);
  210. return drive;
  211. }
  212. PathString PathString::GetDir() const
  213. {
  214. PathString dir;
  215. SplitPath(nullptr, &dir, nullptr, nullptr);
  216. return dir;
  217. }
  218. PathString PathString::GetPath() const
  219. {
  220. PathString drive, dir;
  221. SplitPath(&drive, &dir, nullptr, nullptr);
  222. return drive + dir;
  223. }
  224. PathString PathString::GetFileName() const
  225. {
  226. PathString fname;
  227. SplitPath(nullptr, nullptr, &fname, nullptr);
  228. return fname;
  229. }
  230. PathString PathString::GetFileExt() const
  231. {
  232. PathString ext;
  233. SplitPath(nullptr, nullptr, nullptr, &ext);
  234. return ext;
  235. }
  236. PathString PathString::GetFullFileName() const
  237. {
  238. PathString name, ext;
  239. SplitPath(nullptr, nullptr, &name, &ext);
  240. return name + ext;
  241. }
  242. bool PathString::IsDirectory() const
  243. {
  244. // Using PathIsDirectoryW here instead would increase libopenmpt dependencies by shlwapi.dll.
  245. // GetFileAttributesW also does the job just fine.
  246. #if MPT_OS_WINDOWS_WINRT
  247. WIN32_FILE_ATTRIBUTE_DATA data = {};
  248. if(::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0)
  249. {
  250. return false;
  251. }
  252. DWORD dwAttrib = data.dwFileAttributes;
  253. #else // !MPT_OS_WINDOWS_WINRT
  254. DWORD dwAttrib = ::GetFileAttributes(path.c_str());
  255. #endif // MPT_OS_WINDOWS_WINRT
  256. return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
  257. }
  258. bool PathString::IsFile() const
  259. {
  260. #if MPT_OS_WINDOWS_WINRT
  261. WIN32_FILE_ATTRIBUTE_DATA data = {};
  262. if (::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0)
  263. {
  264. return false;
  265. }
  266. DWORD dwAttrib = data.dwFileAttributes;
  267. #else // !MPT_OS_WINDOWS_WINRT
  268. DWORD dwAttrib = ::GetFileAttributes(path.c_str());
  269. #endif // MPT_OS_WINDOWS_WINRT
  270. return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
  271. }
  272. #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
  273. #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
  274. bool PathString::FileOrDirectoryExists() const
  275. {
  276. return ::PathFileExists(path.c_str()) != FALSE;
  277. }
  278. #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
  279. #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
  280. PathString PathString::ReplaceExt(const mpt::PathString &newExt) const
  281. {
  282. return GetDrive() + GetDir() + GetFileName() + newExt;
  283. }
  284. PathString PathString::SanitizeComponent() const
  285. {
  286. PathString result = *this;
  287. SanitizeFilename(result);
  288. return result;
  289. }
  290. // Convert an absolute path to a path that's relative to "&relativeTo".
  291. PathString PathString::AbsolutePathToRelative(const PathString &relativeTo) const
  292. {
  293. mpt::PathString result = *this;
  294. if(path.empty())
  295. {
  296. return result;
  297. }
  298. if(!_tcsncicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), relativeTo.AsNative().length()))
  299. {
  300. // Path is OpenMPT's directory or a sub directory ("C:\OpenMPT\Somepath" => ".\Somepath")
  301. result = P_(".\\"); // ".\"
  302. result += mpt::PathString::FromNative(AsNative().substr(relativeTo.AsNative().length()));
  303. } else if(!_tcsncicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), 2))
  304. {
  305. // Path is on the same drive as OpenMPT ("C:\Somepath" => "\Somepath")
  306. result = mpt::PathString::FromNative(AsNative().substr(2));
  307. }
  308. return result;
  309. }
  310. // Convert a path that is relative to "&relativeTo" to an absolute path.
  311. PathString PathString::RelativePathToAbsolute(const PathString &relativeTo) const
  312. {
  313. mpt::PathString result = *this;
  314. if(path.empty())
  315. {
  316. return result;
  317. }
  318. if(path.length() >= 2 && path[0] == PC_('\\') && path[1] != PC_('\\'))
  319. {
  320. // Path is on the same drive as OpenMPT ("\Somepath\" => "C:\Somepath\"), but ignore network paths starting with "\\"
  321. result = mpt::PathString::FromNative(relativeTo.AsNative().substr(0, 2));
  322. result += mpt::PathString(path);
  323. } else if(path.length() >= 2 && path.substr(0, 2) == PL_(".\\"))
  324. {
  325. // Path is OpenMPT's directory or a sub directory (".\Somepath\" => "C:\OpenMPT\Somepath\")
  326. result = relativeTo; // "C:\OpenMPT\"
  327. result += mpt::PathString::FromNative(AsNative().substr(2));
  328. }
  329. return result;
  330. }
  331. #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
  332. bool PathString::IsPathSeparator(RawPathString::value_type c)
  333. {
  334. #if MPT_OS_WINDOWS
  335. return (c == PC_('\\')) || (c == PC_('/'));
  336. #else
  337. return c == PC_('/');
  338. #endif
  339. }
  340. RawPathString::value_type PathString::GetDefaultPathSeparator()
  341. {
  342. #if MPT_OS_WINDOWS
  343. return PC_('\\');
  344. #else
  345. return PC_('/');
  346. #endif
  347. }
  348. } // namespace mpt
  349. namespace mpt
  350. {
  351. bool PathIsAbsolute(const mpt::PathString &path) {
  352. mpt::RawPathString rawpath = path.AsNative();
  353. #if MPT_OS_WINDOWS
  354. if(rawpath.substr(0, 8) == PL_("\\\\?\\UNC\\"))
  355. {
  356. return true;
  357. }
  358. if(rawpath.substr(0, 4) == PL_("\\\\?\\"))
  359. {
  360. return true;
  361. }
  362. if(rawpath.substr(0, 2) == PL_("\\\\"))
  363. {
  364. return true; // UNC
  365. }
  366. if(rawpath.substr(0, 2) == PL_("//"))
  367. {
  368. return true; // UNC
  369. }
  370. return (rawpath.length()) >= 3 && (rawpath[1] == ':') && mpt::PathString::IsPathSeparator(rawpath[2]);
  371. #else
  372. return (rawpath.length() >= 1) && mpt::PathString::IsPathSeparator(rawpath[0]);
  373. #endif
  374. }
  375. #if MPT_OS_WINDOWS
  376. #if !(MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00))
  377. mpt::PathString GetAbsolutePath(const mpt::PathString &path)
  378. {
  379. DWORD size = GetFullPathName(path.AsNative().c_str(), 0, nullptr, nullptr);
  380. if(size == 0)
  381. {
  382. return path;
  383. }
  384. std::vector<TCHAR> fullPathName(size, TEXT('\0'));
  385. if(GetFullPathName(path.AsNative().c_str(), size, fullPathName.data(), nullptr) == 0)
  386. {
  387. return path;
  388. }
  389. return mpt::PathString::FromNative(fullPathName.data());
  390. }
  391. #endif
  392. #ifdef MODPLUG_TRACKER
  393. bool DeleteWholeDirectoryTree(mpt::PathString path)
  394. {
  395. if(path.AsNative().empty())
  396. {
  397. return false;
  398. }
  399. if(PathIsRelative(path.AsNative().c_str()) == TRUE)
  400. {
  401. return false;
  402. }
  403. if(!path.FileOrDirectoryExists())
  404. {
  405. return true;
  406. }
  407. if(!path.IsDirectory())
  408. {
  409. return false;
  410. }
  411. path.EnsureTrailingSlash();
  412. HANDLE hFind = NULL;
  413. WIN32_FIND_DATA wfd = {};
  414. hFind = FindFirstFile((path + P_("*.*")).AsNative().c_str(), &wfd);
  415. if(hFind != NULL && hFind != INVALID_HANDLE_VALUE)
  416. {
  417. do
  418. {
  419. mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName);
  420. if(filename != P_(".") && filename != P_(".."))
  421. {
  422. filename = path + filename;
  423. if(filename.IsDirectory())
  424. {
  425. if(!DeleteWholeDirectoryTree(filename))
  426. {
  427. return false;
  428. }
  429. } else if(filename.IsFile())
  430. {
  431. if(DeleteFile(filename.AsNative().c_str()) == 0)
  432. {
  433. return false;
  434. }
  435. }
  436. }
  437. } while(FindNextFile(hFind, &wfd));
  438. FindClose(hFind);
  439. }
  440. if(RemoveDirectory(path.AsNative().c_str()) == 0)
  441. {
  442. return false;
  443. }
  444. return true;
  445. }
  446. #endif // MODPLUG_TRACKER
  447. #endif // MPT_OS_WINDOWS
  448. #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
  449. mpt::PathString GetExecutablePath()
  450. {
  451. std::vector<TCHAR> exeFileName(MAX_PATH);
  452. while(GetModuleFileName(0, exeFileName.data(), mpt::saturate_cast<DWORD>(exeFileName.size())) >= exeFileName.size())
  453. {
  454. if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  455. {
  456. return mpt::PathString();
  457. }
  458. exeFileName.resize(exeFileName.size() * 2);
  459. }
  460. return mpt::GetAbsolutePath(mpt::PathString::FromNative(exeFileName.data()).GetPath());
  461. }
  462. #if !MPT_OS_WINDOWS_WINRT
  463. mpt::PathString GetSystemPath()
  464. {
  465. DWORD size = GetSystemDirectory(nullptr, 0);
  466. std::vector<TCHAR> path(size + 1);
  467. if(!GetSystemDirectory(path.data(), size + 1))
  468. {
  469. return mpt::PathString();
  470. }
  471. return mpt::PathString::FromNative(path.data()) + P_("\\");
  472. }
  473. #endif // !MPT_OS_WINDOWS_WINRT
  474. #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
  475. #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
  476. mpt::PathString GetTempDirectory()
  477. {
  478. DWORD size = GetTempPath(0, nullptr);
  479. if(size)
  480. {
  481. std::vector<TCHAR> tempPath(size + 1);
  482. if(GetTempPath(size + 1, tempPath.data()))
  483. {
  484. return mpt::PathString::FromNative(tempPath.data());
  485. }
  486. }
  487. // use exe directory as fallback
  488. return mpt::GetExecutablePath();
  489. }
  490. mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix, const mpt::PathString &fileNameExtension)
  491. {
  492. mpt::PathString filename = mpt::GetTempDirectory();
  493. filename += (!fileNamePrefix.empty() ? fileNamePrefix + P_("_") : mpt::PathString());
  494. filename += mpt::PathString::FromUnicode(mpt::UUID::GenerateLocalUseOnly(mpt::global_prng()).ToUString());
  495. filename += (!fileNameExtension.empty() ? P_(".") + fileNameExtension : mpt::PathString());
  496. return filename;
  497. }
  498. TempFileGuard::TempFileGuard(const mpt::PathString &filename)
  499. : filename(filename)
  500. {
  501. return;
  502. }
  503. mpt::PathString TempFileGuard::GetFilename() const
  504. {
  505. return filename;
  506. }
  507. TempFileGuard::~TempFileGuard()
  508. {
  509. if(!filename.empty())
  510. {
  511. DeleteFile(filename.AsNative().c_str());
  512. }
  513. }
  514. TempDirGuard::TempDirGuard(const mpt::PathString &dirname_)
  515. : dirname(dirname_.WithTrailingSlash())
  516. {
  517. if(dirname.empty())
  518. {
  519. return;
  520. }
  521. if(::CreateDirectory(dirname.AsNative().c_str(), NULL) == 0)
  522. { // fail
  523. dirname = mpt::PathString();
  524. }
  525. }
  526. mpt::PathString TempDirGuard::GetDirname() const
  527. {
  528. return dirname;
  529. }
  530. TempDirGuard::~TempDirGuard()
  531. {
  532. if(!dirname.empty())
  533. {
  534. DeleteWholeDirectoryTree(dirname);
  535. }
  536. }
  537. #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
  538. } // namespace mpt
  539. #if defined(MODPLUG_TRACKER)
  540. static inline char SanitizeFilenameChar(char c)
  541. {
  542. if( c == '\\' ||
  543. c == '\"' ||
  544. c == '/' ||
  545. c == ':' ||
  546. c == '?' ||
  547. c == '<' ||
  548. c == '>' ||
  549. c == '|' ||
  550. c == '*')
  551. {
  552. c = '_';
  553. }
  554. return c;
  555. }
  556. static inline wchar_t SanitizeFilenameChar(wchar_t c)
  557. {
  558. if( c == L'\\' ||
  559. c == L'\"' ||
  560. c == L'/' ||
  561. c == L':' ||
  562. c == L'?' ||
  563. c == L'<' ||
  564. c == L'>' ||
  565. c == L'|' ||
  566. c == L'*')
  567. {
  568. c = L'_';
  569. }
  570. return c;
  571. }
  572. #if MPT_CXX_AT_LEAST(20)
  573. static inline char8_t SanitizeFilenameChar(char8_t c)
  574. {
  575. if( c == u8'\\' ||
  576. c == u8'\"' ||
  577. c == u8'/' ||
  578. c == u8':' ||
  579. c == u8'?' ||
  580. c == u8'<' ||
  581. c == u8'>' ||
  582. c == u8'|' ||
  583. c == u8'*')
  584. {
  585. c = u8'_';
  586. }
  587. return c;
  588. }
  589. #endif
  590. void SanitizeFilename(mpt::PathString &filename)
  591. {
  592. mpt::RawPathString tmp = filename.AsNative();
  593. for(auto &c : tmp)
  594. {
  595. c = SanitizeFilenameChar(c);
  596. }
  597. filename = mpt::PathString::FromNative(tmp);
  598. }
  599. void SanitizeFilename(char *beg, char *end)
  600. {
  601. for(char *it = beg; it != end; ++it)
  602. {
  603. *it = SanitizeFilenameChar(*it);
  604. }
  605. }
  606. void SanitizeFilename(wchar_t *beg, wchar_t *end)
  607. {
  608. for(wchar_t *it = beg; it != end; ++it)
  609. {
  610. *it = SanitizeFilenameChar(*it);
  611. }
  612. }
  613. void SanitizeFilename(std::string &str)
  614. {
  615. for(size_t i = 0; i < str.length(); i++)
  616. {
  617. str[i] = SanitizeFilenameChar(str[i]);
  618. }
  619. }
  620. void SanitizeFilename(std::wstring &str)
  621. {
  622. for(size_t i = 0; i < str.length(); i++)
  623. {
  624. str[i] = SanitizeFilenameChar(str[i]);
  625. }
  626. }
  627. #if MPT_USTRING_MODE_UTF8
  628. void SanitizeFilename(mpt::u8string &str)
  629. {
  630. for(size_t i = 0; i < str.length(); i++)
  631. {
  632. str[i] = SanitizeFilenameChar(str[i]);
  633. }
  634. }
  635. #endif // MPT_USTRING_MODE_UTF8
  636. #if defined(MPT_WITH_MFC)
  637. void SanitizeFilename(CString &str)
  638. {
  639. for(int i = 0; i < str.GetLength(); i++)
  640. {
  641. str.SetAt(i, SanitizeFilenameChar(str.GetAt(i)));
  642. }
  643. }
  644. #endif // MPT_WITH_MFC
  645. #endif // MODPLUG_TRACKER
  646. #if defined(MODPLUG_TRACKER)
  647. mpt::PathString FileType::AsFilterString(FlagSet<FileTypeFormat> format) const
  648. {
  649. mpt::PathString filter;
  650. if(GetShortName().empty() || GetExtensions().empty())
  651. {
  652. return filter;
  653. }
  654. if(!GetDescription().empty())
  655. {
  656. filter += mpt::PathString::FromUnicode(GetDescription());
  657. } else
  658. {
  659. filter += mpt::PathString::FromUnicode(GetShortName());
  660. }
  661. const auto extensions = GetExtensions();
  662. if(format[FileTypeFormatShowExtensions])
  663. {
  664. filter += P_(" (");
  665. bool first = true;
  666. for(const auto &ext : extensions)
  667. {
  668. if(first)
  669. {
  670. first = false;
  671. } else
  672. {
  673. filter += P_(",");
  674. }
  675. filter += P_("*.");
  676. filter += ext;
  677. }
  678. filter += P_(")");
  679. }
  680. filter += P_("|");
  681. {
  682. bool first = true;
  683. for(const auto &ext : extensions)
  684. {
  685. if(first)
  686. {
  687. first = false;
  688. } else
  689. {
  690. filter += P_(";");
  691. }
  692. filter += P_("*.");
  693. filter += ext;
  694. }
  695. }
  696. filter += P_("|");
  697. return filter;
  698. }
  699. mpt::PathString FileType::AsFilterOnlyString() const
  700. {
  701. mpt::PathString filter;
  702. const auto extensions = GetExtensions();
  703. {
  704. bool first = true;
  705. for(const auto &ext : extensions)
  706. {
  707. if(first)
  708. {
  709. first = false;
  710. } else
  711. {
  712. filter += P_(";");
  713. }
  714. filter += P_("*.");
  715. filter += ext;
  716. }
  717. }
  718. return filter;
  719. }
  720. mpt::PathString ToFilterString(const FileType &fileType, FlagSet<FileTypeFormat> format)
  721. {
  722. return fileType.AsFilterString(format);
  723. }
  724. mpt::PathString ToFilterString(const std::vector<FileType> &fileTypes, FlagSet<FileTypeFormat> format)
  725. {
  726. mpt::PathString filter;
  727. for(const auto &type : fileTypes)
  728. {
  729. filter += type.AsFilterString(format);
  730. }
  731. return filter;
  732. }
  733. mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty)
  734. {
  735. mpt::PathString filter = fileType.AsFilterOnlyString();
  736. return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? P_(";") : P_("")) + filter;
  737. }
  738. mpt::PathString ToFilterOnlyString(const std::vector<FileType> &fileTypes, bool prependSemicolonWhenNotEmpty)
  739. {
  740. mpt::PathString filter;
  741. for(const auto &type : fileTypes)
  742. {
  743. filter += type.AsFilterOnlyString();
  744. }
  745. return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? P_(";") : P_("")) + filter;
  746. }
  747. #endif // MODPLUG_TRACKER
  748. OPENMPT_NAMESPACE_END