namespace util {
tl::expected<void, std::string>
-copy_file(const std::string& src,
- const std::string& dest,
- ViaTmpFile via_tmp_file)
+copy_file(const fs::path& src, const fs::path& dest, ViaTmpFile via_tmp_file)
{
- Fd src_fd(open(src.c_str(), O_RDONLY | O_BINARY));
+ Fd src_fd(open(src.string().c_str(), O_RDONLY | O_BINARY));
if (!src_fd) {
return tl::unexpected(
FMT("Failed to open {} for reading: {}", src, strerror(errno)));
}
- unlink(dest.c_str());
+ unlink(dest.string().c_str());
Fd dest_fd;
- std::string tmp_file;
+ fs::path tmp_file;
if (via_tmp_file == ViaTmpFile::yes) {
TemporaryFile temp_file(dest);
dest_fd = std::move(temp_file.fd);
tmp_file = temp_file.path;
} else {
- dest_fd =
- Fd(open(dest.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
+ dest_fd = Fd(open(
+ dest.string().c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
if (!dest_fd) {
return tl::unexpected(
FMT("Failed to open {} for writing: {}", dest, strerror(errno)));
}
void
-create_cachedir_tag(const std::string& dir)
+create_cachedir_tag(const fs::path& dir)
{
constexpr char cachedir_tag[] =
"Signature: 8a477f597d28d172789f06886806bc55\n"
"# For information about cache directory tags, see:\n"
"#\thttp://www.brynosaurus.com/cachedir/\n";
- const std::string path = FMT("{}/CACHEDIR.TAG", dir);
- if (DirEntry(path).exists()) {
+ auto path = dir / "CACHEDIR.TAG";
+ if (fs::exists(path)) {
return;
}
const auto result = write_file(path, cachedir_tag);
template<typename T>
tl::expected<T, std::string>
-read_file(const std::string& path, size_t size_hint)
+read_file(const fs::path& path, size_t size_hint)
{
if (size_hint == 0) {
DirEntry de(path);
return O_RDONLY | O_BINARY;
}
}();
- Fd fd(open(path.c_str(), open_flags));
+ Fd fd(open(path.string().c_str(), open_flags));
if (!fd) {
return tl::unexpected(strerror(errno));
}
return result;
}
-template tl::expected<Bytes, std::string> read_file(const std::string& path,
+template tl::expected<Bytes, std::string> read_file(const fs::path& path,
size_t size_hint);
-template tl::expected<std::string, std::string>
-read_file(const std::string& path, size_t size_hint);
+template tl::expected<std::string, std::string> read_file(const fs::path& path,
+ size_t size_hint);
template tl::expected<std::vector<uint8_t>, std::string>
-read_file(const std::string& path, size_t size_hint);
+read_file(const fs::path& path, size_t size_hint);
template<typename T>
tl::expected<T, std::string>
-read_file_part(const std::string& path, size_t pos, size_t count)
+read_file_part(const fs::path& path, size_t pos, size_t count)
{
T result;
if (count == 0) {
return result;
}
- Fd fd(open(path.c_str(), O_RDONLY | O_BINARY));
+ Fd fd(open(path.string().c_str(), O_RDONLY | O_BINARY));
if (!fd) {
LOG("Failed to open {}: {}", path, strerror(errno));
return tl::unexpected(strerror(errno));
}
template tl::expected<Bytes, std::string>
-read_file_part(const std::string& path, size_t pos, size_t count);
+read_file_part(const fs::path& path, size_t pos, size_t count);
template tl::expected<std::string, std::string>
-read_file_part(const std::string& path, size_t pos, size_t count);
+read_file_part(const fs::path& path, size_t pos, size_t count);
template tl::expected<std::vector<uint8_t>, std::string>
-read_file_part(const std::string& path, size_t pos, size_t count);
+read_file_part(const fs::path& path, size_t pos, size_t count);
tl::expected<bool, std::error_code>
-remove(const std::string& path, LogFailure log_failure)
+remove(const fs::path& path, LogFailure log_failure)
{
auto result = fs::remove(path);
if (result || log_failure == LogFailure::yes) {
}
tl::expected<bool, std::error_code>
-remove_nfs_safe(const std::string& path, LogFailure log_failure)
+remove_nfs_safe(const fs::path& path, LogFailure log_failure)
{
// fs::remove isn't atomic if path is on an NFS share, so we rename to a
// temporary file. We don't care if the temporary file is trashed, so it's
// always safe to remove it first.
- std::string tmp_name =
- FMT("{}.ccache{}remove", path, TemporaryFile::tmp_file_infix);
+ auto tmp_path =
+ path.parent_path()
+ / FMT("{}.ccache{}remove", path.filename(), TemporaryFile::tmp_file_infix);
- auto rename_result = fs::rename(path, tmp_name);
+ auto rename_result = fs::rename(path, tmp_path);
if (!rename_result) {
// It's OK if it was removed in a race.
if (rename_result.error().value() != ENOENT
&& rename_result.error().value() != ESTALE
&& log_failure == LogFailure::yes) {
- LOG("Removing {} via {}", path, tmp_name);
+ LOG("Removing {} via {}", path, tmp_path);
LOG("Renaming {} to {} failed: {}",
path,
- tmp_name,
+ tmp_path,
rename_result.error().message());
}
return tl::unexpected(rename_result.error());
}
- auto remove_result = fs::remove(tmp_name);
+ auto remove_result = fs::remove(tmp_path);
if (remove_result || log_failure == LogFailure::yes) {
- LOG("Removing {} via {}", path, tmp_name);
+ LOG("Removing {} via {}", path, tmp_path);
if (!remove_result) {
LOG("Removal failed: {}", remove_result.error().message());
}
}
void
-set_timestamps(const std::string& path,
+set_timestamps(const fs::path& path,
std::optional<TimePoint> mtime,
std::optional<TimePoint> atime)
{
atime_mtime[0] = (atime ? *atime : *mtime).to_timespec();
atime_mtime[1] = mtime->to_timespec();
}
- utimensat(AT_FDCWD, path.c_str(), mtime ? atime_mtime : nullptr, 0);
+ utimensat(AT_FDCWD, path.string().c_str(), mtime ? atime_mtime : nullptr, 0);
#elif defined(HAVE_UTIMES)
timeval atime_mtime[2];
if (mtime) {
atime_mtime[1].tv_sec = mtime->sec();
atime_mtime[1].tv_usec = mtime->nsec_decimal_part() / 1000;
}
- utimes(path.c_str(), mtime ? atime_mtime : nullptr);
+ utimes(path.string().c_str(), mtime ? atime_mtime : nullptr);
#else
utimbuf atime_mtime;
if (mtime) {
atime_mtime.actime = atime ? atime->sec() : mtime->sec();
atime_mtime.modtime = mtime->sec();
- utime(path.c_str(), &atime_mtime);
+ utime(path.string().c_str(), &atime_mtime);
} else {
- utime(path.c_str(), nullptr);
+ utime(path.string().c_str(), nullptr);
}
#endif
}
#ifdef HAVE_DIRENT_H
tl::expected<void, std::string>
-traverse_directory(const std::string& directory,
+traverse_directory(const fs::path& directory,
const TraverseDirectoryVisitor& visitor)
{
- DIR* dir = opendir(directory.c_str());
+ DIR* dir = opendir(directory.string().c_str());
if (!dir) {
return tl::unexpected(
FMT("Failed to traverse {}: {}", directory, strerror(errno)));
continue;
}
- std::string entry_path = directory + "/" + entry->d_name;
+ auto path = directory / entry->d_name;
bool is_dir;
# ifdef _DIRENT_HAVE_D_TYPE
if (entry->d_type != DT_UNKNOWN) {
} else
# endif
{
- DirEntry dir_entry(entry_path);
+ DirEntry dir_entry(path);
if (!dir_entry) {
if (dir_entry.error_number() == ENOENT
|| dir_entry.error_number() == ESTALE) {
continue;
}
- return tl::unexpected(FMT("Failed to lstat {}: {}",
- entry_path,
- strerror(dir_entry.error_number())));
+ return tl::unexpected(FMT(
+ "Failed to lstat {}: {}", path, strerror(dir_entry.error_number())));
}
is_dir = dir_entry.is_directory();
}
if (is_dir) {
- traverse_directory(entry_path, visitor);
+ traverse_directory(path, visitor);
} else {
- visitor(entry_path, false);
+ visitor(path.string(), false);
}
}
- visitor(directory, true);
+ visitor(directory.string(), true);
return {};
}
#else // If not available, use the C++17 std::filesystem implementation.
tl::expected<void, std::string>
-traverse_directory(const std::string& directory,
+traverse_directory(const fs::path& directory,
const TraverseDirectoryVisitor& visitor)
{
// Note: Intentionally not using std::filesystem::recursive_directory_iterator
visitor(entry.path().string(), entry.is_directory());
}
}
- visitor(directory, true);
+ visitor(directory.string(), true);
} catch (const std::filesystem::filesystem_error& e) {
return tl::unexpected(e.what());
}
}
tl::expected<void, std::string>
-write_file(const std::string& path, std::string_view data, InPlace in_place)
+write_file(const fs::path& path, std::string_view data, InPlace in_place)
{
if (in_place == InPlace::no) {
- unlink(path.c_str());
+ unlink(path.string().c_str());
}
- Fd fd(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0666));
+ Fd fd(
+ open(path.string().c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_TEXT, 0666));
if (!fd) {
return tl::unexpected(strerror(errno));
}
}
tl::expected<void, std::string>
-write_file(const std::string& path,
+write_file(const fs::path& path,
nonstd::span<const uint8_t> data,
InPlace in_place)
{
if (in_place == InPlace::no) {
- unlink(path.c_str());
+ unlink(path.string().c_str());
}
- Fd fd(open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
+ Fd fd(
+ open(path.string().c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666));
if (!fd) {
return tl::unexpected(strerror(errno));
}
#include <cstddef>
#include <cstdint>
#include <ctime>
+#include <filesystem>
#include <functional>
#include <optional>
#include <string>
// Copy a file from `src` to `dest`. If `via_tmp_file` is yes, `src` is copied
// to a temporary file and then renamed to dest.
tl::expected<void, std::string>
-copy_file(const std::string& src,
- const std::string& dest,
+copy_file(const std::filesystem::path& src,
+ const std::filesystem::path& dest,
ViaTmpFile via_tmp_file = ViaTmpFile::no);
-void create_cachedir_tag(const std::string& dir);
+void create_cachedir_tag(const std::filesystem::path& dir);
// Extends file size of `fd` to at least `new_size` by calling posix_fallocate()
// if supported, otherwise by writing zeros last to the file.
// If `size_hint` is not 0 then it is assumed that `path` has this size (this
// saves system calls).
template<typename T>
-tl::expected<T, std::string> read_file(const std::string& path,
+tl::expected<T, std::string> read_file(const std::filesystem::path& path,
size_t size_hint = 0);
// Return (at most) `count` bytes from `path` starting at position `pos`.
// UTF-8.
template<typename T>
tl::expected<T, std::string>
-read_file_part(const std::string& path, size_t pos, size_t count);
+read_file_part(const std::filesystem::path& path, size_t pos, size_t count);
// Remove `path` (non-directory), NFS hazardous. Use only for files that will
// not exist on other systems.
// Returns whether the file was removed. A nonexistent `path` is considered
// successful.
tl::expected<bool, std::error_code>
-remove(const std::string& path, LogFailure log_failure = LogFailure::yes);
+remove(const std::filesystem::path& path,
+ LogFailure log_failure = LogFailure::yes);
// Remove `path` (non-directory), NFS safe.
//
// Returns whether the file was removed. A nonexistent `path` is considered a
// successful.
tl::expected<bool, std::error_code>
-remove_nfs_safe(const std::string& path,
+remove_nfs_safe(const std::filesystem::path& path,
LogFailure log_failure = LogFailure::yes);
// Set the FD_CLOEXEC on file descriptor `fd`. This is a NOP on Windows.
// Set atime/mtime of `path`. If `mtime` is std::nullopt, set to the current
// time. If `atime` is std::nullopt, set to what `mtime` specifies.
-void set_timestamps(const std::string& path,
+void set_timestamps(const std::filesystem::path& path,
std::optional<TimePoint> mtime = std::nullopt,
std::optional<TimePoint> atime = std::nullopt);
// Traverse `path` recursively in postorder (directory entries are visited
// before their parent directory).
tl::expected<void, std::string>
-traverse_directory(const std::string& directory,
+traverse_directory(const std::filesystem::path& directory,
const TraverseDirectoryVisitor& visitor);
// Write `size` bytes from binary `data` to `fd`.
// Write text `data` to `path`. If `in_place` is no, unlink any existing file
// first (i.e., break hard links).
-tl::expected<void, std::string> write_file(const std::string& path,
+tl::expected<void, std::string> write_file(const std::filesystem::path& path,
std::string_view data,
InPlace in_place = InPlace::no);
// Write binary `data` to `path`. If `in_place` is no, unlink any existing
// file first (i.e., break hard links).
-tl::expected<void, std::string> write_file(const std::string& path,
+tl::expected<void, std::string> write_file(const std::filesystem::path& path,
nonstd::span<const uint8_t> data,
InPlace in_place = InPlace::no);