Loading files
9d2aa90beThis file is pinned to the selected release. Download the original to open it in your design tool.
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBREPCB_CORE_FILEPATH_H
#define LIBREPCB_CORE_FILEPATH_H
/*******************************************************************************
* Includes
******************************************************************************/
#include <QtCore>
/*******************************************************************************
* Namespace / Forward Declarations
******************************************************************************/
namespace librepcb {
/*******************************************************************************
* Class FilePath
******************************************************************************/
/**
* @brief This class represents absolute, well-formatted paths to files or
* directories
*
*
* <b>Why we need well-formatted paths and what do they look like</b>
*
* Using QString for paths to directories or files works not bad, but there are
* some disadventages. As the receiver of a filepath in form of a QString,
* you...
* - never know whether it uses native separators or not ("/" or "\", depends
* on the OS)
* - never know whether it is an absolute or a relative filepath
* - never know whether it contains redundant separators ("//" or "\\")
* - never know whether it contains "." or ".." entries
* - never know whether a filepath to a directory ends with a separator or not
*
* This means, you have to make a lot of checks at runtime to verify the
* filepath. But that's annoying and can lead into problems if you do not make
* these checks always solid.
*
* The class ::librepcb::FilePath provides a way to avoid such problems and
* tries to reduce the count of runtime checks you have to perform yourself
* (the checks will be performed anyway, but the class #FilePath will do this
* for you).
*
* For compatibility reasons between different operating systems, we should
* follow these rules for all paths in the whole project (if not explicitly
* needed other formats):
* - Always use absolute paths, never relative paths (relative paths can be
* dangerous!)
* - Always use "/" as directory separator (UNIX style), never "\" (Windows
* style)
* - A filepath to a directory must never end with a slash (except the UNIX
* root "/")
* - Never use redundant separators ("//") in paths
* - Never use "." and ".." in paths
*
* @note For convenience, a filepath which satisfies ALL these rules is called a
* "well formatted filepath" in this documentation.
*
* To reach this goal, you need to convert each filepath which comes from
* outside the application (user input, read from file, ...) immediately into
* such a well-formatted filepath (make absolute, convert separators, remove
* redundant entries and so on). Then the filepath can be processed by the
* application. If a filepath must be printed to outside the application (print
* to a message box, write to a file, ...), the filepath can be converted (back)
* to the needed format (maybe "\" instead of "/", maybe relative instead of
* absolute, ...).
*
*
* <b>How the class ::librepcb::FilePath works and how to use it</b>
*
* The class ::librepcb::FilePath represents a well-formatted filepath to a file
* or directory and provides methods to convert paths between different formats.
* Every ::librepcb::FilePath object represents either a well-formatted filepath
* or an invalid object (see #isValid()). It's not possible to create
* ::librepcb::FilePath objects with non-well-formatted filepaths.
*
* <b>Example:</b>
* @code
* FilePath fp("C:\\foo\\bar.txt"); // a file
* qDebug(fp.toStr()); // "C:/foo/bar.txt"
* qDebug(fp.toNative()); // "C:/foo/bar.txt"; Windows: "C:\\foo\\bar.txt"
* fp.setPath("/foo/bar/"); // a directory
* qDebug(fp.toStr()); // "/foo/bar" (trailing slash removed!)
* qDebug(fp.toNative()); // "/foo/bar" ("\\foo\\bar" on Windows)
* @endcode
*
* @note A filepath represented by a FilePath object do not need to exist on
* the file system. Most methods of this class do not depend on whether a
* filepath exists or not. Exceptions: #toUnique(), #isExistingFile(),
* #isExistingDir().
*
* @warning Please consider that the conversion from filepaths with backslashes
* as directory separator (Windows style) to filepaths with slashes as directory
* separator (UNIX style, well-formatted paths) will work only on
* windows! The reason is that backslashes are allowed in UNIX filepaths (and
* are NOT interpreted as directory separator), so it's not allowed to replace
* all backslashes in a UNIX filepath with slashes!
*
* @warning Be careful with special characters in filepaths! Especially
* (relative) filepaths in the library or in projects must always work on all
* operating systems (the whole workspace must be platform independent!). This
* means that only characters are allowed which can be used in filepaths on all
* operating systems (for example the backslash "\" is allowed in UNIX
* filenames, but not in Windows filenames). Other filepaths, like filepaths to
* recently used projects (which are stored in the user's profile), do not need
* to be platform independent.
*/
class FilePath final {
public: // Types
enum CleanFileNameOption {
// spaces
KeepSpaces = 0,
ReplaceSpaces = 1 << 1,
// case
KeepCase = 0,
ToLowerCase = 1 << 2,
ToUpperCase = 1 << 3,
// default
Default = KeepSpaces | KeepCase,
};
Q_DECLARE_FLAGS(CleanFileNameOptions, CleanFileNameOption)
public: // Methods
// Constructors / Destructor
/**
* @brief The default constructor (this will create an invalid object!)
*/
FilePath() noexcept;
/**
* @brief Constructor to create a ::librepcb::FilePath object from a QString
*
* @param filepath See #setPath()
*/
explicit FilePath(const QString& filepath) noexcept;
/**
* @brief The copy constructor
*
* @param other The object to copy
*/
FilePath(const FilePath& other) noexcept;
// Setters
/**
* @brief Set a new filepath
*
* @param filepath An absolute (!) filepath to a file or directory (the
* target do not need to exist). On Windows, both forward ("/") and backward
* ("\") slashes are allowed as directory separators (also mixed in one
* filepath). On other operating systems, only forward slashes ("/") are
* allowed!. Also ".", ".." and redundant directory separators are allowed.
*
* @return true on success, false on error (then this object will be invalid!)
*/
bool setPath(const QString& filepath) noexcept;
// Getters
/**
* @brief Check whether this object contains a valid filepath or not
*
* @return true if the filepath is valid, false if not
*/
bool isValid() const noexcept { return mIsValid; }
/**
* @brief Check if the specified filepath is an existing file
*
* @return true if the path points to an existing file, false otherwise
*/
bool isExistingFile() const noexcept;
/**
* @brief Check if the specified filepath is an existing directory
*
* @return true if the path points to an existing directory, false otherwise
*/
bool isExistingDir() const noexcept;
/**
* @brief Check if the specified filepath is an existing, empty directory
*
* @return true if the path points to an existing, empty directory, false
* otherwise
*/
bool isEmptyDir() const noexcept;
/**
* @brief Check if the specified filepath is the root directory
*
* @return True if the filepath is the filesystem root, false otherwise
*/
bool isRoot() const noexcept;
/**
* @brief Check if the filepath is located inside another directory
*
* @return True if the filepath points to an item inside "dir", false
* otherwise
*/
bool isLocatedInDir(const FilePath& dir) const noexcept;
/**
* @brief Get the absolute and well-formatted filepath as a QString
*
* @return The absolute and well-formatted filepath, or an empty QString if
* this object is invalid.
*/
QString toStr() const noexcept;
/**
* @brief Get the absolute filepath with native directory separators
*
* @return The same as #toStr(), but with native separators (on other
* platforms than Windows, this method is identical to #toStr())
*/
QString toNative() const noexcept;
/**
* @brief Get a unique version of the filepath (resolve symbolic links if
* possible)
*
* Because of symbolic links, the user is able to have different paths which
* all point to the same file/directory. But sometimes you want to determine
* whether two paths point to the same file/directory ("equal paths") or not
* ("different paths"). For this purpose you can use #toUnique(). This method
* will resolve symbolic links if possible (this is only possible if the
* filepath exists!). If the filepath does not exist, this method will return
* the same as #toStr().
*
* @return The filepath with resolved symbolic links (if possible, otherwise
* the unchanged filepath)
*/
FilePath toUnique() const noexcept;
/**
* @brief Convert an absolute filepath to a relative filepath (relative to
* another filepath)
*
* @param base The base of the relative filepath (the part which will be
* removed from the absolute filepath). This must be a filepath to a
* directory, paths to a file will produce wrong results!
*
* @return A relative filepath with "/" as directory separators (can contain
* "../")
*
* @note This method is very useful to store relative paths in (text) files.
*
* @see #toRelativeNative(), #fromRelative()
*/
QString toRelative(const FilePath& base) const noexcept;
/**
* @brief Same as #toRelative(), but with native directory separators
*
* @param base See #toRelative().
*
* @return See #toRelative(), but with native directory separators.
*
* @see #toRelative(), #fromRelative()
*/
QString toRelativeNative(const FilePath& base) const noexcept;
/**
* @brief Create and return a QUrl object with this filepath
*
* @return QUrl object which points to this local file (QUrl::fromLocalFile())
*/
QUrl toQUrl() const noexcept { return QUrl::fromLocalFile(toStr()); }
/**
* @brief Get the basename of the file or directory
*
* @return The filename before the first '.' character
*/
QString getBasename() const noexcept;
/**
* @brief Get the complete basename of the file or directory
*
* @return The filename before the last '.' character
*/
QString getCompleteBasename() const noexcept;
/**
* @brief Get the suffix of the file or directory
*
* @return The filename after the last '.' character
*/
QString getSuffix() const noexcept;
/**
* @brief Get the complete suffix of the file or directory
*
* @return The filename after the first '.' character
*/
QString getCompleteSuffix() const noexcept;
/**
* @brief Get the whole filename (without the path) of the file or directory
*
* @return The whole filename
*/
QString getFilename() const noexcept;
/**
* @brief Get the filepath of the parent directory of the file or directory
*
* @return A FilePath object with the parent directory (can be invalid if you
* try to get the parent directory of the filesystem root!)
*/
FilePath getParentDir() const noexcept;
/**
* @brief Get the filepath to a file or directory which is relative to this
* filepath
*
* @param filename A relative filepath to a file or directory,
* like "file.txt" or "subdir/file.txt"
*
* @return A FilePath object to the specified file or directory
*
* @warning This method works only correct if this filepath represents a
* directory!
*
* @note This method is equal to
* `librepcb::FilePath::fromRelative(*this, filename);`
*/
FilePath getPathTo(const QString& filename) const noexcept;
// Static Methods
/**
* @brief Build an absolute and well-formatted filepath from a relative
* filepath
*
* @param base The base of the relative filepath (the part which is
* missed from the absolute filepath). This must be a filepath to a directory,
* paths to a file will produce wrong results!
* @param relative The relative path (relative to "base")
*
* @return A ::librepcb::FilePath object with the absolute filepath
*
* @note This method is very useful to load relative paths from (text) files.
*
* @see #toRelative()
*/
static FilePath fromRelative(const FilePath& base,
const QString& relative) noexcept;
/**
* @brief Get the path to the temporary directory (e.g. "/tmp" on Unix/Linux)
*
* @attention Do not use this to store any files in it!
* Use #getRandomTempPath() instead.
*
* @return The filepath (in case of an error, the path can be invalid!)
*/
static FilePath getTempPath() noexcept;
/**
* @brief Get the path to the temporary application directory (e.g.
* "/tmp/librepcb")
*
* @attention Do not use this to store any files in it!
* Use #getRandomTempPath() instead.
*
* @return The filepath (in case of an error, the path can be invalid!)
*/
static FilePath getApplicationTempPath() noexcept;
/**
* @brief Get a random temporary directory path (e.g. "/tmp/librepcb/42")
*
* @return The random filepath (in case of an error, the path can be invalid!)
*/
static FilePath getRandomTempPath() noexcept;
/**
* @brief Clean a given string so that it becomes a valid filename
*
* Every time a file- or directory name needs to be constructed (e.g. from
* user input), you must use this function to replace/remove all characters
* which are not allowed for file/dir paths.
*
* These are the only allowed characters: "-._ 0-9A-Za-z"
*
* In addition, the length of the filename will be limited to 120 characters.
*
* @param userInput An arbitrary string (may be directly from a user input
* field)
* @param options Some options to define how the filename should be escaped
*
* @return A string which is either empty or a valid filename (based on
* userInput)
*
* @note This function does exectly the same on all supported platforms,
* even if the set of allowed characters depends on the platform. This way we
* can guarantee that all created files/directories are platform
* independent.
*/
static QString cleanFileName(const QString& userInput,
CleanFileNameOptions options) noexcept;
// Operator Overloadings
/**
* @brief The assign operator to copy a FilePath into another FilePath object
*/
FilePath& operator=(const FilePath& rhs) noexcept;
/**
* @brief The "==" operator to compare two FilePath objects
*
* @note This method compares the return values of #toStr() of both objects.
*
* @return true if both filepaths are identical, false otherwise
*/
bool operator==(const FilePath& rhs) const noexcept;
/**
* @brief The "!=" operator to compare two FilePath objects
*
* @note This method compares the return values of #toStr() of both objects.
*
* @return false if both filepaths are identical, true otherwise
*/
bool operator!=(const FilePath& rhs) const noexcept;
/**
* @brief The "<" operator to compare two FilePath objects
*
* Useful for sorting filepaths, or to store them in a QSet.
*
* @note This method compares the return values of #toStr() of both objects.
*
* @param rhs The right hand side object.
*
* @return true if this filepath is smaller, else false
*/
bool operator<(const FilePath& rhs) const noexcept;
private:
// Private Methods
/**
* @brief Make a filepath well-formatted (except making it absolute!)
*
* @param filepath An absolute or relative filepath which may isn't
* well-formatted
*
* @return A filepath which satisfies all rules for well-formatted paths,
* except that it is absolute (it can be relative!)
*/
static QString makeWellFormatted(const QString& filepath) noexcept;
// Attributes
bool mIsValid;
QFileInfo
mFileInfo; ///< the absolute and well-formatted filepath in a QFileInfo
};
// Non-Member Functions
QDataStream& operator<<(QDataStream& stream, const FilePath& filepath);
QDebug& operator<<(QDebug& stream, const FilePath& filepath);
inline std::size_t qHash(const FilePath& key, std::size_t seed = 0) noexcept {
return qHash(key.toStr(), seed);
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace librepcb
Q_DECLARE_METATYPE(librepcb::FilePath)
Q_DECLARE_OPERATORS_FOR_FLAGS(librepcb::FilePath::CleanFileNameOptions)
#endif