blob: 5de62b2b307093b24ec8ee248d5a9cdfad33e75b [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
Tomáš Pecka5be83e42021-04-21 17:26:40 +020081void writeFile(const std::string& path, const std::string_view& contents)
82{
83 std::ofstream ofs(path);
84 if (!ofs.is_open()) {
85 throw std::invalid_argument("File '" + std::string(path) + "' could not be opened.");
86 }
87
88 if (!(ofs << contents)) {
89 throw std::invalid_argument("File '" + std::string(path) + "' could not be written.");
90 }
91}
92
Václav Kubernátbabbab92021-01-27 09:25:05 +010093void safeWriteFile(const std::string& filename, const std::string_view& contents)
94{
95 auto throwErr = [&filename] (const auto& what) {
96 throw std::runtime_error(fmt::format("Couldn't write file '{}' ({}) ({})", filename, what, std::strerror(errno)));
97 };
98 // FIXME: not sure if just the tilde is fine...
99 auto tempFileName = (filename + "~");
100 auto f = std::fopen(tempFileName.c_str(), "w");
101 if (!f) {
102 throwErr("fopen");
103 }
104 if (std::fwrite(contents.data(), contents.size(), 1, f) != 1) {
105 throwErr("fwrite");
106 }
107 if (fsync(fileno(f)) == -1) {
108 throwErr("fsync");
109 }
110 if (std::fclose(f) == -1) {
111 throwErr("fclose");
112 }
113
114 try {
115 std::filesystem::rename(tempFileName.c_str(), filename.c_str());
116 } catch (std::filesystem::filesystem_error&) {
117 throwErr("rename");
118 }
119
120 auto dirName = std::filesystem::path(filename).parent_path();
121 auto fdir = std::fopen(dirName.c_str(), "r");
122 if (!fdir) {
123 throwErr("fopen");
124 }
125 if (fsync(fileno(fdir)) == -1) {
126 throwErr("fsync");
127 }
128 if (std::fclose(fdir) == -1) {
129 throwErr("fclose");
130 }
131}
Tomáš Pecka491f4f22020-11-05 15:44:00 +0100132}