#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2021 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_STORAGE_FILESYSTEM_PATH_H #define NUCLEX_STORAGE_FILESYSTEM_PATH_H #include "Nuclex/Storage/Config.h" #include // std::string // Possible additions: // // - MakeRelative() -> Probably not, all my asset paths will be relative anyway // // May be useful for content authoring tools (level editors) where asset paths // should be saved as relative paths // // /// Makes a path relative to the specified base // /// Base to which the path will be made relative // /// Absolute path that will be made relative // /// The relative path as seen to from the specified base directory // public: NUCLEX_STORAGE_API static Path MakeRelative( // const Path &base, const Path &absolute // ); // // - MakeAbsolute() -> Nope, use Resolve()! // // May be useful for command line tools that want to make sure inputs specified // on the command line are used under their absolute paths // // Already covered by Resolve() and a bad idea because on some operating systems, // additional information ("what is the working directory on drive E:?") may be // needed, making this method untestable and dependent on process state. // // /// Makes the specified path absolute // /// // /// Base path starting at which the relative path will be evaluated // /// // /// Path relative to the current working directory // /// The absolute path // public: NUCLEX_STORAGE_API static Path MakeAbsolute(const Path &base, const Path &relative); // // - GetParent() -> Maybe.. // // If a file browser is implemented, this could be used for the "go up" command // // public: NUCLEX_STORAGE_API static std::string GetParent(const std::string &path); // // - GetDirectory() -> Probably not, I don't need to fiddle around with paths like this // // public: NUCLEX_STORAGE_API static std::string GetDirectory(const std::string &path); // // - Multi-input Join() -> Yes! Makes many things simpler // // private: template // using are_same = std::conjunction...>; // // template< // typename... TCombinedPaths, // typename = std::enable_if_t< // std::conjunction...>::value, void // > // > // void print_same_type(const Path &basePath, const TCombinedPaths... &combinedPaths) {} // namespace Nuclex { namespace Storage { namespace FileSystem { // ------------------------------------------------------------------------------------------- // /// Path to a file or directory /// /// This class provides common utility methods to combine and split paths. /// All methods assume UTF-8 strings and are implemented inside this library /// to ensure repeatable and reliable behavior independent of C runtime and /// host OS character sets. /// class Path { /// A path that references the current directory public: NUCLEX_STORAGE_API static const std::string CurrentDirectory; /// A path that references the parent directory public: NUCLEX_STORAGE_API static const std::string ParentDirectory; /// Character(s) that will be used to separate directory names in a path /// /// This is the character you find between a directory and a filename or a directory /// and a subdirectory in a path. It is not the character you find in the PATH variable /// or other list as a separator between absolute paths. /// public: NUCLEX_STORAGE_API const static std::string DirectorySeparator; /// Checks if the specified path is an absolute path /// Path that will be checked /// True if the path is absolute, false if it is relative public: NUCLEX_STORAGE_API static bool IsAbsolute(const std::string &path); /// Checks if the specified path is a relative path /// Path that will be checked /// True if the path is relative, false if it is absolute public: NUCLEX_STORAGE_API static bool IsRelative(const std::string &path); /// Figures out the stem of a path /// Path of which the stem will be determined /// The stem of the specified part /// /// /// The stem of the path is those characters that will be not change no matter /// how many directory levels you go 'up'. For example, on Linux systems, /// the parent of '~/test/file.txt' is '~/test', then '~' then '~/..'. /// The '~' remains. Same with a drive letter on Windows or the URI scheme /// in a URI-like path (i.e. 'http://' is typically the stem there). /// /// /// This handles plain paths, URI-like paths and UNC paths or drive letters /// on Windows. A server name in UNC paths or a host name in URI-like paths /// is not seen as part of the stem. /// /// public: NUCLEX_STORAGE_API static std::string GetStem(const std::string &path); /// Extracts the filename from a path /// Path the filename will be extracted from /// The filename contained in the path /// /// /// This method does exactly what you would expect it to do. For dot files /// (files that start with a dot, used on Linux systems for hidden files), /// the dot is seen as part of the filename. /// /// /// If a file has chained extensions (such as 'archive.tar.gz' or 'config.xml.zip'), /// the entire chain is seen as the extension, so the name of the example files /// from before would be 'archive' and 'config'. /// /// public: NUCLEX_STORAGE_API static std::string GetFilename( const std::string &path, bool withExtension = true ); /// Extracts the file extension from a path /// Path from which the extension will be extracted /// The extension of the filename in the specified path public: NUCLEX_STORAGE_API static std::string GetExtension(const std::string &path); /// Eliminates all removable parent directory references in a path /// Path that will be normalized /// The path with all removable parent directory references eliminated /// /// /// This cleans any unneccessary parent directory references from a path, so /// '/home/me/../../etc/cfg' will become '/etc/cfg'. /// /// /// Use this method with care. Assume, for example, a directory contained a symlink /// to a directory in an entirely different place. The path '~/symlink/..' would /// be normalized to '~', but the original path was actually going up from whatever /// directory the symlink pointed to. /// /// /// If you want the ultimate path, followed through symlinks, shortcuts and everything /// to its ultimate destination, use the method instead. /// This method is faster and allows independent verification in unit tests for paths /// you have control over (i.e. below the game's install directory - it doesn't matter /// if the install directory or anything above it is a symlink as it will never be left) /// /// public: NUCLEX_STORAGE_API static std::string Normalize(const std::string &path); /// Establishes the ultimate target a path is referencing /// Path that will be resolved /// /// /// The ultimate target of the specified path. If the input is a relative path, /// it will be turned absolute using the current working directory (if applicable /// on the current platform), all directory separators will change to the platform's /// native format and any symlinks will be traveled. /// /// /// Use this if you need to resolve paths that may become a goose chase through /// the platform's file system through symlinks and parent directory references. /// /// public: NUCLEX_STORAGE_API static std::string Resolve(const std::string &path); /// Joins two paths /// Path to which the other path will be appended /// Path that will be joined to the base path /// The combined path /// /// /// This method blindly appends one path to the other. The trailing path separator /// will be taken care of, but if the second path is an absolute one, an invalid /// path will result. /// /// /// Whether the resulting path ends in a directory separator is up to the joined /// path (or the base path if the joined path is empty). This method only takes /// care of inserting a directory separator between the two paths if it is needed. /// /// public: NUCLEX_STORAGE_API static std::string Join( const std::string &base, const std::string &other ); /// Combines two paths /// Path that will serve as a base for the combined path /// Path that will be combined with the base path /// The combined path /// /// This combines two paths by finding the target ultimately pointed to by /// the joined paths (for example, joining 'a/b/c' and '../d' results in /// 'a/b/d' rather than 'a/b/c/../d' as it would with ). /// It does not make relative paths absolute or vice versa. /// public: NUCLEX_STORAGE_API static std::string Combine( const std::string &base, const std::string &other ); /// Determines the length of the path's stem /// Path in which the stem length is determined /// The index at which the path's directory chain begins /// /// This checks for the part of the path that can not be removed by going up, /// i.e. you can't go up from 'D:\\' or from '/' or from 'file://' by removing /// this part of the path. For some paths, the stem length will be 0 (i.e. you /// can go up from 'hello/' to './' or even further to '../', so the stem length /// here is zero). /// private: static std::string::size_type getStemLength(const std::string &path); /// Figures out the character at which the filename begins /// Path in which the beginning of the filename will be found /// The index of the first filename character private: static std::string::size_type getFilenameStartIndex(const std::string &path); }; // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Storage::FileSystem #endif // NUCLEX_STORAGE_FILESYSTEM_PATH_H