code/include/swoc/swoc_file.h (252 lines of code) (raw):

// SPDX-License-Identifier: Apache-2.0 // Copyright Apache Software Foundation 2019 /** @file Simple path and file utilities. */ #pragma once #include <unistd.h> #include <sys/stat.h> #include <string> #include <string_view> #include <system_error> #include <chrono> #include "swoc/swoc_version.h" #include "swoc/TextView.h" namespace swoc { inline namespace SWOC_VERSION_NS { namespace file { using file_time_type = std::chrono::system_clock::time_point; enum class file_type : signed char { none = 0, not_found = -1, regular = 1, directory = 2, symlink = 3, block = 4, character = 5, fifo = 6, socket = 7, unknown = 8 }; /// Invalid file descriptor. static constexpr int NO_FD = -1; /// Scoped container for a file descriptor. struct unique_fd { using self_type = unique_fd; ///< Self reference type. /** Construct with file descriptor. * * @param fd File descriptor. * * The instance takes owernship of the file descriptor. */ explicit unique_fd(int fd) : _fd(fd) {} /// Non-copyable. unique_fd(self_type const &) = delete; /** Move construction. * * @param that Source instance. * * Ownership is from from @Wa that to @a this. */ unique_fd(self_type &&that) : _fd(that._fd) { that._fd = NO_FD; } /// Close the file dscriptor. ~unique_fd() { if (_fd != NO_FD) { ::close(_fd); _fd = NO_FD; }; } /** Release ownership of the file descriptor. * * @return The file descriptor. */ int release() { auto zret = _fd; _fd = NO_FD; return zret; } /// Implicitly convert to file descriptor. operator int() const { return _fd; } protected: int _fd = NO_FD; }; /** Utility class for file system paths. */ class path { using self_type = path; ///< Self reference type. public: /// Default path separator. static constexpr char SEPARATOR = '/'; /// Default construct empty path. path() = default; /// Copy constructor - copies the path. path(const self_type &that) = default; /// Move constructor. path(self_type &&that) = default; /// Construct from a null terminated string. explicit path(const char *src); /// Construct from a string view. path(std::string_view src); /// Construct with a copy of @a p. path(std::string const &p); /// Move from an existing string path(std::string &&that); /// Replace the path with a copy of @a that. self_type &operator=(const self_type &that) = default; /// Replace the path with the contents of @a that. self_type &operator=(self_type &&that) = default; /// Assign @a p as the path. self_type &operator=(std::string_view p); /// Assign @a s as the path. self_type &operator=(std::string const &s); /// Move @a s to be the path. self_type &operator=(std::string &&s); /// Copy @a s as the path. self_type &operator=(char const *s); /** Append or replace path with @a that. * * If @a that is absolute, it replaces @a this. Otherwise @a that is appended with exactly one * separator. * * @param that Filesystem path. * @return @a this */ self_type &operator/=(const self_type &that); /** Append or replace path with @a that. * * If @a that is absolute, it replaces @a this. Otherwise @a that is appended with exactly one * separator. * * @param that Filesystem path. * @return @a this */ self_type &operator/=(std::string_view that); /// Check if the path is empty. bool empty() const; /// Check if the path is absolute. bool is_absolute() const; /// Check if the path is not absolute. bool is_relative() const; /// Path of the parent. self_type parent_path() const; /// @return Path excluding the root path, if nay. self_type relative_path() const; /** Filename part of the path. * * @param p Path. * @return */ self_type filename() const; /// Access the path explicitly. char const *c_str() const; /// The path as a string. std::string const &string() const; /** Reserve space in the path. * * @param n Number of bytes to reserve. * @return @a this. */ self_type &reserve(size_t n); /// A view of the path. swoc::TextView view() const; protected: std::string _path; ///< File path. }; /// Information about a file. class file_status { using self_type = file_status; public: /// @return File type as an enumeration. file_type type() const; /// @return raw file mode data. mode_t mode() const; protected: struct ::stat _stat; ///< File information. file_type _type = file_type::none; void init(); friend self_type status(const path &file, std::error_code &ec) noexcept; friend int file_type(const self_type &); friend uintmax_t file_size(const self_type &); friend bool is_regular_file(const self_type &); friend bool is_dir(const self_type &); friend bool is_char_device(const self_type &); friend bool is_block_device(const self_type &); friend bool exists(const self_type &); friend file_time_type last_write_time(file_status const &fs); friend file_time_type access_time(file_status const &fs); friend file_time_type status_time(file_status const &fs); }; /** Get the status of the file at @a p. * * @param file Path to file. * @param ec Error code return. * @return Status of the file. */ file_status status(const path &file, std::error_code &ec) noexcept; // Related free functions. // These are separate because they are not part of std::filesystem::path. /// Return the file type value. [[deprecated("Use file_status::type")]] int file_type(const file_status &fs); /// Check if the path is to a regular file. bool is_regular_file(const file_status &fs); /// Check if the path is to a directory. bool is_dir(const file_status &p); /// Check if the path is to a character device. bool is_char_device(const file_status &fs); /// Check if the path is to a block device. bool is_block_device(const file_status &fs); /// Size of the file or block device. uintmax_t file_size(const file_status &fs); /// Check if file is readable. bool is_readable(const path &s); /// Check if path exists. bool exists(const path &p); /// Check if path exists. bool exists(const file_status &fs); /** Convert to absolute path. * * @param src Original path * @param ec Error code. * @return Absolute path. * * If @a path is already absolute, a copy of it is returned. Otherwise an absolute path is * constructed that refers to the same item in the file system as @a src. If an error occurs * then @a ec is set to indicate the type of error. */ path absolute(path const &src, std::error_code &ec); /// Directory location suitable for temporary files path temp_directory_path(); /// Current working directory. path current_path(); /// @return Canonicalized absolute pathname path canonical(const path &p, std::error_code &ec); /** Create directory. * * @param p Path to directory. * @param ec Error code return. * @param mode Permissions for created directory. * @return @c true if @a p was created, @c false otherwise. * * @note Not fully implemented. */ bool create_directory(const path &p, std::error_code &ec, mode_t mode = 0775) noexcept; /** Create directories. * * @param p Path to directory. * @param ec Error code return. * @param mode Permissions for created directories. * @return @c true if @a p was created, @c false otherwise. * * @note Not fully implemented. */ bool create_directories(const path &p, std::error_code &ec, mode_t mode = 0775) noexcept; /** Copy files. * * @param from Source file * @param to Destination file. * @param ec Error code return. * @return @c true if @a from was copied, @c false on error. * * @note Not fully implemented. */ bool copy(const path &from, const path &to, std::error_code &ec); /** Remove a file or empty directory. * * @param path Target. * @param ec Error return. * @return @c true if the operation succeeded, @c false if @a ec has an error code. */ bool remove(const path &path, std::error_code &ec); /** Remove a file or a directory and all nested files. * * @param path Target. * @param ec Error return. * @return Number of items removed. */ uintmax_t remove_all(const path &path, std::error_code &ec); /// @return The modified time for @a fs. [[deprecated("See last_write_time")]] file_time_type modify_time(file_status const &fs); /// @return The modified time for @a fs. file_time_type last_write_time(file_status const &fs); /** Modification time. * * @param p Path to target. * @return Time of last modification, or @c file_time_type::min() on error. */ file_time_type last_write_time(path const &p, std::error_code &ec); /// @return The access time for @a fs. file_time_type access_time(file_status const &fs); /// @return The status change time for @a fs. file_time_type status_time(file_status const &fs); /** Load the file at @a p into a @c std::string. * * @param p Path to file * @param ec Error code result of the file operation. * @return The contents of the file. */ std::string load(const path &p, std::error_code &ec); /* ------------------------------------------------------------------- */ inline path::path(char const *src) : _path(src) {} inline path::path(std::string_view base) : _path(base) {} inline path::path(std::string const &p) : _path(p) {} inline path::path(std::string &&that) : _path(std::move(that)) {} inline path & path::operator=(std::string_view p) { _path.assign(p); return *this; } inline path & path::operator=(std::string const &s) { _path.assign(s); return *this; } inline path & path::operator=(std::string &&s) { _path.assign(std::move(s)); return *this; } inline path & path::operator=(char const *s) { _path.assign(s); return *this; } inline char const * path::c_str() const { return _path.c_str(); } inline std::string const & path::string() const { return _path; } inline swoc::TextView path::view() const { return {_path}; } inline bool path::empty() const { return _path.empty(); } inline bool path::is_absolute() const { return !_path.empty() && '/' == _path[0]; } inline bool path::is_relative() const { return !this->is_absolute(); } inline path & path::operator/=(const self_type &that) { return *this /= std::string_view(that._path); } /** Compare two paths. * * @return @c true if @a lhs is identical to @a rhs. */ inline bool operator==(path const &lhs, path const &rhs) { return lhs.view() == rhs.view(); } /** Compare two paths. * * @return @c true if @a lhs is not identical to @a rhs. */ inline bool operator!=(path const &lhs, path const &rhs) { return lhs.view() != rhs.view(); } /** Combine two strings as file paths. * * @return A @c path with the combined path. */ inline path operator/(const path &lhs, const path &rhs) { return path(lhs) /= rhs; } /** Combine two strings as file paths. * * @return A @c path with the combined path. */ inline path operator/(path &&lhs, const path &rhs) { return path(std::move(lhs)) /= rhs; } /** Combine two strings as file paths. * * @return A @c path with the combined path. */ inline path operator/(const path &lhs, std::string_view rhs) { return path(lhs) /= rhs; } inline auto path::reserve(size_t n) -> self_type & { _path.reserve(n); return *this; } /** Combine two strings as file paths. * * @return A @c path with the combined path. */ inline path operator/(path &&lhs, std::string_view rhs) { return path(std::move(lhs)) /= rhs; } // Can remove "enum" after the function @c file_type is removed. inline enum file_type file_status::type() const { return _type; } inline mode_t file_status::mode() const { return _stat.st_mode; } inline bool is_char_device(const file_status &fs) { return fs._type == file_type::character; } inline bool is_block_device(const file_status &fs) { return fs._type == file_type::block; } inline bool is_regular_file(const file_status &fs) { return fs._type == file_type::regular; } inline bool is_dir(const file_status &fs) { return fs._type == file_type::directory; } inline bool exists(const file_status &fs) { return fs._type != file_type::none && fs._type != file_type::not_found; } inline file_time_type modify_time(file_status const &fs) { return last_write_time(fs); } } // namespace file class BufferWriter; namespace bwf { struct Spec; } BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, file::path const &p); }} // namespace swoc::SWOC_VERSION_NS namespace std { /// Enable use of path as a key in STL hashed containers. template <> struct hash<swoc::file::path> { size_t operator()(swoc::file::path const &path) const { return hash<string_view>()(path.view()); } }; } // namespace std