# endif
#endif
-#include <algorithm>
-#include <climits>
-#include <codecvt>
-#include <fstream>
-#include <locale>
-
using IncludeDelimiter = util::Tokenizer::IncludeDelimiter;
namespace {
return result;
}
-#ifdef _WIN32
-bool
-has_utf16_le_bom(std::string_view text)
-{
- return text.size() > 1
- && ((static_cast<uint8_t>(text[0]) == 0xff
- && static_cast<uint8_t>(text[1]) == 0xfe));
-}
-#endif
-
} // namespace
namespace Util {
return static_cast<uint64_t>(result);
}
-bool
-read_fd(int fd, DataReceiver data_receiver)
-{
- int64_t n;
- char buffer[CCACHE_READ_BUFFER_SIZE];
- while ((n = read(fd, buffer, sizeof(buffer))) != 0) {
- if (n == -1 && errno != EINTR) {
- break;
- }
- if (n > 0) {
- data_receiver(buffer, n);
- }
- }
- return n >= 0;
-}
-
-std::string
-read_file(const std::string& path, size_t size_hint)
-{
- if (size_hint == 0) {
- auto stat = Stat::stat(path);
- if (!stat) {
- throw core::Error(strerror(errno));
- }
- size_hint = stat.size();
- }
-
- // +1 to be able to detect EOF in the first read call
- size_hint = (size_hint < 1024) ? 1024 : size_hint + 1;
-
- Fd fd(open(path.c_str(), O_RDONLY | O_BINARY));
- if (!fd) {
- throw core::Error(strerror(errno));
- }
-
- int64_t ret = 0;
- size_t pos = 0;
- std::string result;
- result.resize(size_hint);
-
- while (true) {
- if (pos == result.size()) {
- result.resize(2 * result.size());
- }
- const size_t max_read = result.size() - pos;
- ret = read(*fd, &result[pos], max_read);
- if (ret == 0 || (ret == -1 && errno != EINTR)) {
- break;
- }
- if (ret > 0) {
- pos += ret;
- if (static_cast<size_t>(ret) < max_read) {
- break;
- }
- }
- }
-
- if (ret == -1) {
- LOG("Failed reading {}", path);
- throw core::Error(strerror(errno));
- }
-
- result.resize(pos);
- return result;
-}
-
#ifndef _WIN32
std::string
read_link(const std::string& path)
return resolved ? resolved : (return_empty_on_error ? "" : path);
}
-std::string
-read_text_file(const std::string& path, size_t size_hint)
-{
- std::string result = read_file(path, size_hint);
-#ifdef _WIN32
- // Convert to UTF-8 if the content starts with a UTF-16 little-endian BOM.
- //
- // Note that this code assumes a little-endian machine, which is why it's
- // #ifdef-ed to only run on Windows (which is always little-endian) where it's
- // actually needed.
- if (has_utf16_le_bom(result)) {
- result.erase(0, 2); // Remove BOM.
- std::u16string result_as_u16((result.size() / 2) + 1, '\0');
- result_as_u16 = reinterpret_cast<const char16_t*>(result.c_str());
- std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
- result = converter.to_bytes(result_as_u16);
- }
-#endif
- return result;
-}
-
std::string_view
remove_extension(std::string_view path)
{
});
}
-void
-write_fd(int fd, const void* data, size_t size)
-{
- int64_t written = 0;
- do {
- const auto count =
- write(fd, static_cast<const uint8_t*>(data) + written, size - written);
- if (count == -1) {
- if (errno != EAGAIN && errno != EINTR) {
- throw core::Error(strerror(errno));
- }
- } else {
- written += count;
- }
- } while (static_cast<size_t>(written) < size);
-}
-
-void
-write_file(const std::string& path,
- const std::string& data,
- std::ios_base::openmode open_mode)
-{
- if (path.empty()) {
- throw core::Error("No such file or directory");
- }
-
- open_mode |= std::ios::out;
- std::ofstream file(path, open_mode);
- if (!file) {
- throw core::Error(strerror(errno));
- }
- file << data;
-}
-
} // namespace Util
#include <cstdint>
#include <filesystem>
#include <functional>
-#include <ios>
#include <memory>
#include <optional>
#include <string>
namespace Util {
-using DataReceiver = std::function<void(const void* data, size_t size)>;
using TraverseVisitor =
std::function<void(const std::string& path, bool is_dir)>;
// is also recognized as a synonym of k. Throws `core::Error` on parse error.
uint64_t parse_size(const std::string& value);
-// Read data from `fd` until end of file and call `data_receiver` with the read
-// data. Returns whether reading was successful, i.e. whether the read(2) call
-// did not return -1.
-bool read_fd(int fd, DataReceiver data_receiver);
-
-// Return `path`'s content as a string. If `size_hint` is not 0 then assume that
-// `path` has this size (this saves system calls).
-//
-// Throws `core::Error` on error. The description contains the error message
-// without the path.
-std::string read_file(const std::string& path, size_t size_hint = 0);
-
#ifndef _WIN32
// Like readlink(2) but returns the string (or the empty string on failure).
std::string read_link(const std::string& path);
std::string real_path(const std::string& path,
bool return_empty_on_error = false);
-// Return contents of a text file as a UTF-8 encoded string.
-//
-// Throws `core::Error` on error. The description contains the error message
-// without the path.
-std::string read_text_file(const std::string& path, size_t size_hint = 0);
-
// Return a view into `path` containing the given path without the filename
// extension as determined by `get_extension()`.
std::string_view remove_extension(std::string_view path);
// Throws core::Error on error.
void wipe_path(const std::string& path);
-// Write `size` bytes from `data` to `fd`. Throws `core::Error` on error.
-void write_fd(int fd, const void* data, size_t size);
-
-// Write `data` to `path`. The file will be opened according to `open_mode`,
-// which always will include `std::ios::out` even if not specified at the call
-// site.
-//
-// Throws `core::Error` on error. The description contains the error message
-// without the path.
-void write_file(const std::string& path,
- const std::string& data,
- std::ios_base::openmode open_mode = std::ios::binary);
-
} // namespace Util
CHECK_THROWS_WITH(Util::parse_size("10x"), "invalid size: \"10x\"");
}
-TEST_CASE("Util::read_file and Util::write_file")
-{
- TestContext test_context;
-
- Util::write_file("test", "foo\nbar\n");
- std::string data = Util::read_file("test");
- CHECK(data == "foo\nbar\n");
-
- Util::write_file("test", "car");
- data = Util::read_file("test");
- CHECK(data == "car");
-
- Util::write_file("test", "pet", std::ios::app);
- data = Util::read_file("test");
- CHECK(data == "carpet");
-
- Util::write_file("test", "\n", std::ios::app | std::ios::binary);
- data = Util::read_file("test");
- CHECK(data == "carpet\n");
-
- Util::write_file("test", "\n", std::ios::app); // text mode
- data = Util::read_file("test");
-#ifdef _WIN32
- CHECK(data == "carpet\n\r\n");
-#else
- CHECK(data == "carpet\n\n");
-#endif
-
- Util::write_file("size_hint_test", std::string(8192, '\0'));
- CHECK(Util::read_file("size_hint_test", 4096 /*size_hint*/).size() == 8192);
-
- CHECK_THROWS_WITH(Util::read_file("does/not/exist"),
- "No such file or directory");
-
- CHECK_THROWS_WITH(Util::write_file("", "does/not/exist"),
- "No such file or directory");
-
- CHECK_THROWS_WITH(Util::write_file("does/not/exist", "does/not/exist"),
- "No such file or directory");
-}
-
-TEST_CASE("Util::{read,write,copy}_file with binary files")
-{
- TestContext test_context;
-
- std::string data;
- for (size_t i = 0; i < 512; ++i) {
- data.push_back(static_cast<char>((32 + i) % 256));
- }
-
- Util::write_file("test", data);
- CHECK(Util::read_file("test") == data);
-
- Util::copy_file("test", "copy");
- CHECK(Util::read_file("copy") == data);
-}
-
-#ifdef _WIN32
-TEST_CASE("Util::read_text_file with UTF-16 little endian encoding")
-{
- TestContext test_context;
-
- std::string data;
- data.push_back(static_cast<unsigned char>(0xff));
- data.push_back(static_cast<unsigned char>(0xfe));
- data.push_back('a');
- data.push_back('\0');
- data.push_back('b');
- data.push_back('\0');
- data.push_back('c');
- data.push_back('\0');
-
- Util::write_file("test", data);
- CHECK(Util::read_text_file("test") == "abc");
-}
-#endif
-
TEST_CASE("Util::remove_extension")
{
CHECK(Util::remove_extension("") == "");
TestContext test_context;
REQUIRE(Util::create_dir("dir-with-subdir-and-file/subdir"));
- Util::write_file("dir-with-subdir-and-file/subdir/f", "");
+ util::write_file("dir-with-subdir-and-file/subdir/f", "");
REQUIRE(Util::create_dir("dir-with-files"));
- Util::write_file("dir-with-files/f1", "");
- Util::write_file("dir-with-files/f2", "");
+ util::write_file("dir-with-files/f1", "");
+ util::write_file("dir-with-files/f2", "");
REQUIRE(Util::create_dir("empty-dir"));
std::vector<std::string> visited;
SUBCASE("Wipe file")
{
- Util::write_file("a", "");
+ util::write_file("a", "");
CHECK_NOTHROW(Util::wipe_path("a"));
CHECK(!Stat::stat("a"));
}
SUBCASE("Wipe directory")
{
REQUIRE(Util::create_dir("a/b"));
- Util::write_file("a/1", "");
- Util::write_file("a/b/1", "");
+ util::write_file("a/1", "");
+ util::write_file("a/b/1", "");
CHECK_NOTHROW(Util::wipe_path("a"));
CHECK(!Stat::stat("a"));
}