5 #include <c++utilities/conversion/stringbuilder.h>
6 #include <c++utilities/conversion/stringconversion.h>
8 #ifdef PLATFORM_WINDOWS
32 namespace BackupHelper {
53 const std::string &originalPath,
const std::string &backupPath, NativeFileStream &originalStream, NativeFileStream &backupStream)
56 if (originalStream.is_open()) {
57 originalStream.close();
60 backupStream.exceptions(ios_base::goodbit);
63 backupStream.open(backupPath, ios_base::in | ios_base::binary);
64 if (backupStream.is_open()) {
67 throw std::ios_base::failure(
"Backup/temporary file has not been created.");
70 std::remove(originalPath.c_str());
71 if (std::rename(BasicFileInfo::pathForOpen(backupPath), BasicFileInfo::pathForOpen(originalPath))) {
75 backupStream.exceptions(ios_base::failbit | ios_base::badbit);
76 originalStream.exceptions(ios_base::failbit | ios_base::badbit);
77 backupStream.open(backupPath, ios_base::in | ios_base::binary);
78 originalStream.open(originalPath, ios_base::out | ios_base::binary);
79 originalStream << backupStream.rdbuf();
81 }
catch (
const std::ios_base::failure &failure) {
82 throw std::ios_base::failure(
"Unable to restore original file from backup file \"" % backupPath %
"\" after failure: " + failure.what());
90 static bool isRelative(
const std::string &path)
92 return path.empty() || (path.front() !=
'/' && (path.size() < 2 || path[1] !=
':'));
120 void createBackupFile(
const std::string &backupDir,
const std::string &originalPath, std::string &backupPath, NativeFileStream &originalStream,
121 NativeFileStream &backupStream)
124 const auto backupDirRelative(isRelative(backupDir));
125 const auto originalDir(backupDirRelative ? BasicFileInfo::containingDirectory(originalPath) :
string());
128 for (
unsigned int i = 0;; ++i) {
129 if (backupDir.empty()) {
131 backupPath = originalPath %
'.' % i +
".bak";
133 backupPath = originalPath +
".bak";
136 const auto fileName(BasicFileInfo::fileName(originalPath, i));
138 const auto ext(BasicFileInfo::extension(originalPath));
139 if (backupDirRelative) {
140 backupPath = originalDir %
'/' % backupDir %
'/' % fileName %
'.' % i + ext;
142 backupPath = backupDir %
'/' % fileName %
'.' % i + ext;
145 if (backupDirRelative) {
146 backupPath = originalDir %
'/' % backupDir %
'/' + fileName;
148 backupPath = backupDir %
'/' + fileName;
154 #ifdef PLATFORM_WINDOWS
155 if (GetFileAttributes(BasicFileInfo::pathForOpen(backupPath)) == INVALID_FILE_ATTRIBUTES) {
157 struct stat backupStat;
158 if (stat(BasicFileInfo::pathForOpen(backupPath), &backupStat)) {
165 if (originalStream.is_open()) {
166 originalStream.close();
170 if (std::rename(BasicFileInfo::pathForOpen(originalPath), BasicFileInfo::pathForOpen(backupPath))) {
173 backupStream.exceptions(ios_base::failbit | ios_base::badbit);
174 originalStream.exceptions(ios_base::failbit | ios_base::badbit);
176 if (backupStream.is_open()) {
177 backupStream.close();
179 backupStream.open(BasicFileInfo::pathForOpen(backupPath), ios_base::out | ios_base::binary);
181 originalStream.open(BasicFileInfo::pathForOpen(originalPath), ios_base::in | ios_base::binary);
183 backupStream << originalStream.rdbuf();
186 }
catch (
const std::ios_base::failure &failure) {
187 throw std::ios_base::failure(argsToString(
"Unable to rename original file before rewriting it: ", failure.what()));
194 if (originalStream.is_open()) {
195 originalStream.close();
198 if (backupStream.is_open()) {
199 backupStream.close();
202 backupStream.exceptions(ios_base::failbit | ios_base::badbit);
203 backupStream.open(BasicFileInfo::pathForOpen(backupPath), ios_base::in | ios_base::binary);
204 }
catch (
const std::ios_base::failure &failure) {
207 if (std::rename(BasicFileInfo::pathForOpen(backupPath), BasicFileInfo::pathForOpen(originalPath))) {
208 throw std::ios_base::failure(
"Unable to restore original file from backup file \"" % backupPath %
"\" after failure: " + failure.what());
210 throw std::ios_base::failure(argsToString(
"Unable to open backup file: ", failure.what()));
235 NativeFileStream &backupStream,
Diagnostics &diag,
const std::string &context)
246 if (!backupPath.empty()) {
248 diag.emplace_back(DiagLevel::Information,
"Rewriting the file to apply changed tag information has been aborted.", context);
251 diag.emplace_back(DiagLevel::Information,
"The original file has been restored.", context);
252 }
catch (
const std::ios_base::failure &failure) {
253 diag.emplace_back(DiagLevel::Critical, failure.what(), context);
256 diag.emplace_back(DiagLevel::Information,
"Applying new tag information has been aborted.", context);
261 if (!backupPath.empty()) {
263 diag.emplace_back(DiagLevel::Critical,
"Rewriting the file to apply changed tag information failed.", context);
266 diag.emplace_back(DiagLevel::Information,
"The original file has been restored.", context);
267 }
catch (
const std::ios_base::failure &failure) {
268 diag.emplace_back(DiagLevel::Critical, failure.what(), context);
271 diag.emplace_back(DiagLevel::Critical,
"Applying new tag information failed.", context);
275 }
catch (
const std::ios_base::failure &) {
276 if (!backupPath.empty()) {
278 diag.emplace_back(DiagLevel::Critical,
"An IO error occurred when rewriting the file to apply changed tag information.", context);
281 diag.emplace_back(DiagLevel::Information,
"The original file has been restored.", context);
282 }
catch (
const std::ios_base::failure &failure) {
283 diag.emplace_back(DiagLevel::Critical, failure.what(), context);
286 diag.emplace_back(DiagLevel::Critical,
"An IO error occurred when applying tag information.", context);