blob: 7ce41a7f8cf0ea4fa738e37ea2089d3f1e239bbc [file] [log] [blame]
Tomáš Pecka491f4f22020-11-05 15:44:00 +01001/*
2 * Copyright (C) 2020 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
5 *
6*/
7
Václav Kubernátbabbab92021-01-27 09:25:05 +01008#include <cstring>
9#include <fmt/format.h>
Tomáš Pecka491f4f22020-11-05 15:44:00 +010010#include <fstream>
Václav Kubernátbabbab92021-01-27 09:25:05 +010011#include <unistd.h>
Tomáš Pecka491f4f22020-11-05 15:44:00 +010012#include "io.h"
13
14namespace velia::utils {
15
16namespace {
17
18std::ifstream openStream(const std::filesystem::path& path)
19{
20 std::ifstream ifs(path);
21 if (!ifs.is_open())
22 throw std::invalid_argument("File '" + std::string(path) + "' does not exist.");
23 return ifs;
24}
25}
26
27/** @brief Reads a string from a file */
28std::string readFileString(const std::filesystem::path& path)
29{
30 std::ifstream ifs(openStream(path));
31 std::string res;
32
33 if (ifs >> res) {
34 return res;
35 }
36
37 throw std::domain_error("Could not read '" + std::string(path) + "'.");
38}
39
40/** @brief Reads @p valuesCount 32bit values from a file */
41std::vector<uint32_t> readFileWords(const std::filesystem::path& path, int valuesCount)
42{
43 std::ifstream ifs(openStream(path));
44 std::vector<uint32_t> bytes; // no interesting data are wider than 32 bits (for now)
45
46 uint32_t byte;
47 ifs >> std::hex;
48 while (valuesCount-- && ifs >> byte) {
49 bytes.push_back(byte);
50 }
51
52 if (!ifs) {
53 throw std::domain_error("Could not read hex data from '" + std::string(path) + "'.");
54 }
55
56 return bytes;
57}
58
Tomáš Pecka9b1c9672020-11-11 15:24:06 +010059/** @brief Reads a int64_t number from a file. */
60int64_t readFileInt64(const std::filesystem::path& path)
61{
62 std::ifstream ifs(openStream(path));
63 int64_t res;
64
65 if (ifs >> res) {
66 return res;
67 }
68
69 throw std::domain_error("Could not read int64_t value from '" + std::string(path) + "'.");
70}
71
Václav Kubernátbabbab92021-01-27 09:25:05 +010072/** @brief Reads whole contents of `path`. Throws if file doesn't exist. */
73std::string readFileToString(const std::filesystem::path& path)
74{
75 std::ifstream ifs(openStream(path));
76
77 std::istreambuf_iterator<char> begin(ifs), end;
78 return std::string(begin, end);
79}
80
Jan Kundrát3fc09b92024-10-15 15:45:38 +020081/** @brief Read the entire content of `path` into a vector of bytes */
82std::vector<uint8_t> readFileToBytes(const std::filesystem::path& path)
83{
84 std::ifstream ifs(openStream(path));
85 return {std::istreambuf_iterator<char>{ifs}, {}};
86}
87
88
Tomáš Pecka5be83e42021-04-21 17:26:40 +020089void writeFile(const std::string& path, const std::string_view& contents)
90{
91 std::ofstream ofs(path);
92 if (!ofs.is_open()) {
93 throw std::invalid_argument("File '" + std::string(path) + "' could not be opened.");
94 }
95
96 if (!(ofs << contents)) {
97 throw std::invalid_argument("File '" + std::string(path) + "' could not be written.");
98 }
99}
100
Václav Kubernátbabbab92021-01-27 09:25:05 +0100101void safeWriteFile(const std::string& filename, const std::string_view& contents)
102{
103 auto throwErr = [&filename] (const auto& what) {
104 throw std::runtime_error(fmt::format("Couldn't write file '{}' ({}) ({})", filename, what, std::strerror(errno)));
105 };
106 // FIXME: not sure if just the tilde is fine...
107 auto tempFileName = (filename + "~");
108 auto f = std::fopen(tempFileName.c_str(), "w");
109 if (!f) {
110 throwErr("fopen");
111 }
112 if (std::fwrite(contents.data(), contents.size(), 1, f) != 1) {
113 throwErr("fwrite");
114 }
115 if (fsync(fileno(f)) == -1) {
116 throwErr("fsync");
117 }
118 if (std::fclose(f) == -1) {
119 throwErr("fclose");
120 }
121
122 try {
123 std::filesystem::rename(tempFileName.c_str(), filename.c_str());
124 } catch (std::filesystem::filesystem_error&) {
125 throwErr("rename");
126 }
127
128 auto dirName = std::filesystem::path(filename).parent_path();
129 auto fdir = std::fopen(dirName.c_str(), "r");
130 if (!fdir) {
131 throwErr("fopen");
132 }
133 if (fsync(fileno(fdir)) == -1) {
134 throwErr("fsync");
135 }
136 if (std::fclose(fdir) == -1) {
137 throwErr("fclose");
138 }
139}
Tomáš Pecka491f4f22020-11-05 15:44:00 +0100140}