mptPathString.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*
  2. * mptPathString.h
  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. #pragma once
  10. #include "openmpt/all/BuildSettings.hpp"
  11. #include "mptString.h"
  12. #include "mpt/base/namespace.hpp"
  13. #include <vector>
  14. #include "openmpt/base/FlagSet.hpp"
  15. #define MPT_DEPRECATED_PATH
  16. //#define MPT_DEPRECATED_PATH [[deprecated]]
  17. OPENMPT_NAMESPACE_BEGIN
  18. namespace mpt
  19. {
  20. #if MPT_OS_WINDOWS
  21. typedef mpt::winstring RawPathString;
  22. #else // !MPT_OS_WINDOWS
  23. typedef std::string RawPathString;
  24. #endif // if MPT_OS_WINDOWS
  25. class PathString
  26. {
  27. private:
  28. RawPathString path;
  29. private:
  30. explicit PathString(const RawPathString & path_)
  31. : path(path_)
  32. {
  33. return;
  34. }
  35. public:
  36. PathString()
  37. {
  38. return;
  39. }
  40. PathString(const PathString & other)
  41. : path(other.path)
  42. {
  43. return;
  44. }
  45. PathString(PathString && other) noexcept
  46. : path(std::move(other.path))
  47. {
  48. return;
  49. }
  50. PathString & assign(const PathString & other)
  51. {
  52. path = other.path;
  53. return *this;
  54. }
  55. PathString & assign(PathString && other) noexcept
  56. {
  57. path = std::move(other.path);
  58. return *this;
  59. }
  60. PathString & operator = (const PathString & other)
  61. {
  62. return assign(other);
  63. }
  64. PathString &operator = (PathString && other) noexcept
  65. {
  66. return assign(std::move(other));
  67. }
  68. PathString & append(const PathString & other)
  69. {
  70. path.append(other.path);
  71. return *this;
  72. }
  73. PathString & operator += (const PathString & other)
  74. {
  75. return append(other);
  76. }
  77. friend PathString operator + (const PathString & a, const PathString & b)
  78. {
  79. return PathString(a).append(b);
  80. }
  81. friend bool operator < (const PathString & a, const PathString & b)
  82. {
  83. return a.AsNative() < b.AsNative();
  84. }
  85. friend bool operator == (const PathString & a, const PathString & b)
  86. {
  87. return a.AsNative() == b.AsNative();
  88. }
  89. friend bool operator != (const PathString & a, const PathString & b)
  90. {
  91. return a.AsNative() != b.AsNative();
  92. }
  93. bool empty() const { return path.empty(); }
  94. std::size_t Length() const { return path.size(); }
  95. public:
  96. #if MPT_OS_WINDOWS
  97. #if !MPT_OS_WINDOWS_WINRT
  98. static int CompareNoCase(const PathString & a, const PathString & b);
  99. #endif // !MPT_OS_WINDOWS_WINRT
  100. #endif
  101. #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
  102. void SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const;
  103. // \\?\ prefixes will be removed and \\?\\UNC prefixes converted to canonical \\ form.
  104. PathString GetDrive() const; // Drive letter + colon, e.g. "C:" or \\server\\share
  105. PathString GetDir() const; // Directory, e.g. "\OpenMPT\"
  106. PathString GetPath() const; // Drive + Dir, e.g. "C:\OpenMPT\"
  107. PathString GetFileName() const; // File name without extension, e.g. "OpenMPT"
  108. PathString GetFileExt() const; // Extension including dot, e.g. ".exe"
  109. PathString GetFullFileName() const; // File name + extension, e.g. "OpenMPT.exe"
  110. // Verify if this path represents a valid directory on the file system.
  111. bool IsDirectory() const;
  112. // Verify if this path exists and is a file on the file system.
  113. bool IsFile() const;
  114. bool FileOrDirectoryExists() const;
  115. #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
  116. static bool IsPathSeparator(RawPathString::value_type c);
  117. static RawPathString::value_type GetDefaultPathSeparator();
  118. #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
  119. // Return the same path string with a different (or appended) extension (including "."), e.g. "foo.bar",".txt" -> "foo.txt" or "C:\OpenMPT\foo",".txt" -> "C:\OpenMPT\foo.txt"
  120. PathString ReplaceExt(const mpt::PathString &newExt) const;
  121. // Removes special characters from a filename component and replaces them with a safe replacement character ("_" on windows).
  122. // Returns the result.
  123. // Note that this also removes path component separators, so this should only be used on single-component PathString objects.
  124. PathString SanitizeComponent() const;
  125. bool HasTrailingSlash() const
  126. {
  127. if(path.empty())
  128. {
  129. return false;
  130. }
  131. RawPathString::value_type c = path[path.length() - 1];
  132. return IsPathSeparator(c);
  133. }
  134. mpt::PathString &EnsureTrailingSlash()
  135. {
  136. if(!path.empty() && !HasTrailingSlash())
  137. {
  138. path += GetDefaultPathSeparator();
  139. }
  140. return *this;
  141. }
  142. mpt::PathString WithoutTrailingSlash() const
  143. {
  144. mpt::PathString result = *this;
  145. while(result.HasTrailingSlash())
  146. {
  147. if(result.Length() == 1)
  148. {
  149. return result;
  150. }
  151. result = mpt::PathString(result.AsNative().substr(0, result.AsNative().length() - 1));
  152. }
  153. return result;
  154. }
  155. mpt::PathString WithTrailingSlash() const
  156. {
  157. mpt::PathString result = *this;
  158. result.EnsureTrailingSlash();
  159. return result;
  160. }
  161. // Relative / absolute paths conversion
  162. mpt::PathString AbsolutePathToRelative(const mpt::PathString &relativeTo) const;
  163. mpt::PathString RelativePathToAbsolute(const mpt::PathString &relativeTo) const;
  164. #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
  165. public:
  166. #if MPT_OS_WINDOWS
  167. #if !(MPT_WSTRING_CONVERT)
  168. #error "mpt::PathString on Windows depends on MPT_WSTRING_CONVERT)"
  169. #endif
  170. // conversions
  171. #if defined(MPT_ENABLE_CHARSET_LOCALE)
  172. MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::Charset::Locale, path); }
  173. #endif
  174. std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, path); }
  175. std::wstring ToWide() const { return mpt::ToWide(path); }
  176. mpt::ustring ToUnicode() const { return mpt::ToUnicode(path); }
  177. #if defined(MPT_ENABLE_CHARSET_LOCALE)
  178. MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); }
  179. static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); }
  180. #endif
  181. static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::UTF8, path)); }
  182. static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToWin(path)); }
  183. static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWin(path)); }
  184. RawPathString AsNative() const { return path; }
  185. // Return native string, with possible \\?\ prefix if it exceeds MAX_PATH characters.
  186. RawPathString AsNativePrefixed() const;
  187. static PathString FromNative(const RawPathString &path) { return PathString(path); }
  188. #if defined(MPT_WITH_MFC)
  189. // CString TCHAR, so this is CHAR or WCHAR, depending on UNICODE
  190. CString ToCString() const { return mpt::ToCString(path); }
  191. static PathString FromCString(const CString &path) { return PathString(mpt::ToWin(path)); }
  192. #endif // MPT_WITH_MFC
  193. // Convert a path to its simplified form, i.e. remove ".\" and "..\" entries
  194. mpt::PathString Simplify() const;
  195. #else // !MPT_OS_WINDOWS
  196. // conversions
  197. #if defined(MPT_ENABLE_CHARSET_LOCALE)
  198. std::string ToLocale() const { return path; }
  199. std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, mpt::Charset::Locale, path); }
  200. #if MPT_WSTRING_CONVERT
  201. std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::Locale, path); }
  202. #endif
  203. mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::Locale, path); }
  204. static PathString FromLocale(const std::string &path) { return PathString(path); }
  205. static PathString FromLocaleSilent(const std::string &path) { return PathString(path); }
  206. static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, mpt::Charset::UTF8, path)); }
  207. #if MPT_WSTRING_CONVERT
  208. static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); }
  209. #endif
  210. static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); }
  211. RawPathString AsNative() const { return path; }
  212. RawPathString AsNativePrefixed() const { return path; }
  213. static PathString FromNative(const RawPathString &path) { return PathString(path); }
  214. #else // !MPT_ENABLE_CHARSET_LOCALE
  215. std::string ToUTF8() const { return path; }
  216. #if MPT_WSTRING_CONVERT
  217. std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::UTF8, path); }
  218. #endif
  219. mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::UTF8, path); }
  220. static PathString FromUTF8(const std::string &path) { return PathString(path); }
  221. #if MPT_WSTRING_CONVERT
  222. static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); }
  223. #endif
  224. static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); }
  225. RawPathString AsNative() const { return path; }
  226. RawPathString AsNativePrefixed() const { return path; }
  227. static PathString FromNative(const RawPathString &path) { return PathString(path); }
  228. #endif // MPT_ENABLE_CHARSET_LOCALE
  229. // Convert a path to its simplified form (currently only implemented on Windows)
  230. [[deprecated]] mpt::PathString Simplify() const { return PathString(path); }
  231. #endif // MPT_OS_WINDOWS
  232. };
  233. #if defined(MPT_ENABLE_CHARSET_LOCALE)
  234. #if MPT_OS_WINDOWS
  235. #ifdef UNICODE
  236. [[deprecated]] inline std::string ToAString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); }
  237. #else
  238. MPT_DEPRECATED_PATH inline std::string ToAString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.AsNative()); }
  239. #endif
  240. #else
  241. MPT_DEPRECATED_PATH inline std::string ToAString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); }
  242. #endif
  243. #endif
  244. inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); }
  245. #if MPT_WSTRING_FORMAT
  246. inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWide(); }
  247. #endif
  248. } // namespace mpt
  249. #if MPT_OS_WINDOWS
  250. #ifdef UNICODE
  251. #define MPT_PATHSTRING_LITERAL(x) ( L ## x )
  252. #define MPT_PATHSTRING(x) mpt::PathString::FromNative( L ## x )
  253. #else
  254. #define MPT_PATHSTRING_LITERAL(x) ( x )
  255. #define MPT_PATHSTRING(x) mpt::PathString::FromNative( x )
  256. #endif
  257. #else // !MPT_OS_WINDOWS
  258. #define MPT_PATHSTRING_LITERAL(x) ( x )
  259. #define MPT_PATHSTRING(x) mpt::PathString::FromNative( x )
  260. #endif // MPT_OS_WINDOWS
  261. #define PC_(x) MPT_PATHSTRING_LITERAL(x)
  262. #define PL_(x) MPT_PATHSTRING_LITERAL(x)
  263. #define P_(x) MPT_PATHSTRING(x)
  264. namespace mpt
  265. {
  266. bool PathIsAbsolute(const mpt::PathString &path);
  267. #if MPT_OS_WINDOWS
  268. #if !(MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00))
  269. // Returns the absolute path for a potentially relative path and removes ".." or "." components. (same as GetFullPathNameW)
  270. mpt::PathString GetAbsolutePath(const mpt::PathString &path);
  271. #endif
  272. #ifdef MODPLUG_TRACKER
  273. // Deletes a complete directory tree. Handle with EXTREME care.
  274. // Returns false if any file could not be removed and aborts as soon as it
  275. // encounters any error. path must be absolute.
  276. bool DeleteWholeDirectoryTree(mpt::PathString path);
  277. #endif // MODPLUG_TRACKER
  278. #endif // MPT_OS_WINDOWS
  279. #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
  280. // Returns the application executable path or an empty string (if unknown), e.g. "C:\mptrack\"
  281. mpt::PathString GetExecutablePath();
  282. #if !MPT_OS_WINDOWS_WINRT
  283. // Returns the system directory path, e.g. "C:\Windows\System32\"
  284. mpt::PathString GetSystemPath();
  285. #endif // !MPT_OS_WINDOWS_WINRT
  286. #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
  287. #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
  288. // Returns temporary directory (with trailing backslash added) (e.g. "C:\TEMP\")
  289. mpt::PathString GetTempDirectory();
  290. // Returns a new unique absolute path.
  291. mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix = mpt::PathString(), const mpt::PathString &fileNameExtension = P_("tmp"));
  292. // Scoped temporary file guard. Deletes the file when going out of scope.
  293. // The file itself is not created automatically.
  294. class TempFileGuard
  295. {
  296. private:
  297. const mpt::PathString filename;
  298. public:
  299. TempFileGuard(const mpt::PathString &filename = CreateTempFileName());
  300. mpt::PathString GetFilename() const;
  301. ~TempFileGuard();
  302. };
  303. // Scoped temporary directory guard. Deletes the directory when going out of scope.
  304. // The directory itself is created automatically.
  305. class TempDirGuard
  306. {
  307. private:
  308. mpt::PathString dirname;
  309. public:
  310. TempDirGuard(const mpt::PathString &dirname_ = CreateTempFileName());
  311. mpt::PathString GetDirname() const;
  312. ~TempDirGuard();
  313. };
  314. #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
  315. } // namespace mpt
  316. #if defined(MODPLUG_TRACKER)
  317. // Sanitize a filename (remove special chars)
  318. void SanitizeFilename(mpt::PathString &filename);
  319. void SanitizeFilename(char *beg, char *end);
  320. void SanitizeFilename(wchar_t *beg, wchar_t *end);
  321. void SanitizeFilename(std::string &str);
  322. void SanitizeFilename(std::wstring &str);
  323. #if MPT_USTRING_MODE_UTF8
  324. void SanitizeFilename(mpt::u8string &str);
  325. #endif // MPT_USTRING_MODE_UTF8
  326. template <std::size_t size>
  327. void SanitizeFilename(char (&buffer)[size])
  328. {
  329. static_assert(size > 0);
  330. SanitizeFilename(buffer, buffer + size);
  331. }
  332. template <std::size_t size>
  333. void SanitizeFilename(wchar_t (&buffer)[size])
  334. {
  335. static_assert(size > 0);
  336. SanitizeFilename(buffer, buffer + size);
  337. }
  338. #if defined(MPT_WITH_MFC)
  339. void SanitizeFilename(CString &str);
  340. #endif // MPT_WITH_MFC
  341. #endif // MODPLUG_TRACKER
  342. #if defined(MODPLUG_TRACKER)
  343. enum FileTypeFormat
  344. {
  345. FileTypeFormatNone = 0 , // do not show extensions after description, i.e. "Foo Files"
  346. FileTypeFormatShowExtensions = 1<<0, // show extensions after descripten, i.e. "Foo Files (*.foo,*.bar)"
  347. };
  348. MPT_DECLARE_ENUM(FileTypeFormat)
  349. class FileType
  350. {
  351. private:
  352. mpt::ustring m_ShortName; // "flac", "mod" (lowercase)
  353. mpt::ustring m_Description; // "FastTracker 2 Module"
  354. std::vector<std::string> m_MimeTypes; // "audio/ogg" (in ASCII)
  355. std::vector<mpt::PathString> m_Extensions; // "mod", "xm" (lowercase)
  356. std::vector<mpt::PathString> m_Prefixes; // "mod" for "mod.*"
  357. public:
  358. FileType() { }
  359. FileType(const std::vector<FileType> &group)
  360. {
  361. for(const auto &type : group)
  362. {
  363. mpt::append(m_MimeTypes, type.m_MimeTypes);
  364. mpt::append(m_Extensions, type.m_Extensions);
  365. mpt::append(m_Prefixes, type.m_Prefixes);
  366. }
  367. }
  368. static FileType Any()
  369. {
  370. return FileType().ShortName(U_("*")).Description(U_("All Files")).AddExtension(P_("*"));
  371. }
  372. public:
  373. FileType& ShortName(const mpt::ustring &shortName) { m_ShortName = shortName; return *this; }
  374. FileType& Description(const mpt::ustring &description) { m_Description = description; return *this; }
  375. FileType& MimeTypes(const std::vector<std::string> &mimeTypes) { m_MimeTypes = mimeTypes; return *this; }
  376. FileType& Extensions(const std::vector<mpt::PathString> &extensions) { m_Extensions = extensions; return *this; }
  377. FileType& Prefixes(const std::vector<mpt::PathString> &prefixes) { m_Prefixes = prefixes; return *this; }
  378. FileType& AddMimeType(const std::string &mimeType) { m_MimeTypes.push_back(mimeType); return *this; }
  379. FileType& AddExtension(const mpt::PathString &extension) { m_Extensions.push_back(extension); return *this; }
  380. FileType& AddPrefix(const mpt::PathString &prefix) { m_Prefixes.push_back(prefix); return *this; }
  381. public:
  382. mpt::ustring GetShortName() const { return m_ShortName; }
  383. mpt::ustring GetDescription() const { return m_Description; }
  384. std::vector<std::string> GetMimeTypes() const { return m_MimeTypes; }
  385. std::vector<mpt::PathString> GetExtensions() const { return m_Extensions; }
  386. std::vector<mpt::PathString> GetPrefixes() const { return m_Prefixes; }
  387. public:
  388. mpt::PathString AsFilterString(FlagSet<FileTypeFormat> format = FileTypeFormatNone) const;
  389. mpt::PathString AsFilterOnlyString() const;
  390. }; // class FileType
  391. // "Ogg Vorbis|*.ogg;*.oga|" // FileTypeFormatNone
  392. // "Ogg Vorbis (*.ogg,*.oga)|*.ogg;*.oga|" // FileTypeFormatShowExtensions
  393. mpt::PathString ToFilterString(const FileType &fileType, FlagSet<FileTypeFormat> format = FileTypeFormatNone);
  394. mpt::PathString ToFilterString(const std::vector<FileType> &fileTypes, FlagSet<FileTypeFormat> format = FileTypeFormatNone);
  395. // "*.ogg;*.oga" / ";*.ogg;*.oga"
  396. mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty = false);
  397. mpt::PathString ToFilterOnlyString(const std::vector<FileType> &fileTypes, bool prependSemicolonWhenNotEmpty = false);
  398. #endif // MODPLUG_TRACKER
  399. OPENMPT_NAMESPACE_END