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