Loading...
Searching...
No Matches
Bundling.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12
13#include "eon/Bundling.h"
14#include "eon/EonLogger.h"
15
16#include <cctype>
17#include <charconv>
18#include <filesystem>
19#include <format>
20#include <system_error>
21#include <utility>
22
23namespace fs = std::filesystem;
24
25namespace eonc {
26
28 int num_bundle = -1;
29
30 for (const auto &entry : fs::directory_iterator(".")) {
31 std::string name = entry.path().filename().string();
32
33 if (name.empty() || name[0] == '.') {
34 continue;
35 }
36
37 // If "config" is not in the filename
38 // then skip.
39 if (name.find("config") == std::string::npos &&
40 name.find("ini") == std::string::npos) {
41 continue;
42 }
43
44 // Find the last underscore
45 auto upos = name.rfind('_');
46 if (upos == std::string::npos) {
47 continue;
48 }
49
50 // Find the last period
51 auto dpos = name.rfind('.');
52 if (dpos == std::string::npos || dpos <= upos) {
53 continue;
54 }
55
56 std::string numstr = name.substr(upos + 1, dpos - upos - 1);
57 int value = 0;
58 const auto parsed =
59 std::from_chars(numstr.data(), numstr.data() + numstr.size(), value);
60 if (parsed.ec == std::errc{} && parsed.ptr != numstr.data()) {
61 const int i = value + 1;
62 if (i > num_bundle) {
63 num_bundle = i;
64 }
65 }
66 }
67
68 return num_bundle;
69}
70
71std::vector<std::string> unbundle(int number) {
72 std::vector<std::string> filenames;
73
74 for (const auto &entry : fs::directory_iterator(".")) {
75 std::string originalFilename = entry.path().filename().string();
76
77 if (originalFilename.empty() || originalFilename[0] == '.') {
78 continue;
79 }
80
81 if (std::ranges::count(originalFilename, '_') < 1) {
82 continue;
83 }
84
85 // Find the last underscore
86 auto upos = originalFilename.rfind('_');
87 if (upos == std::string::npos) {
88 continue;
89 }
90
91 // Find the last period
92 auto dpos = originalFilename.rfind('.');
93 if (dpos == std::string::npos || dpos <= upos) {
94 continue;
95 }
96
97 // bundle() writes results_3.con.gz. The span from the last underscore
98 // to the last dot is "3.con", so the index is only the digit run.
99 const size_t numBegin = upos + 1;
100 size_t numEnd = numBegin;
101 while (numEnd < originalFilename.size() &&
102 std::isdigit(static_cast<unsigned char>(originalFilename[numEnd]))) {
103 ++numEnd;
104 }
105 if (numEnd == numBegin || numEnd >= originalFilename.size() ||
106 originalFilename[numEnd] != '.') {
107 continue;
108 }
109 int bundleNumber = 0;
110 const auto parsed =
111 std::from_chars(originalFilename.data() + numBegin,
112 originalFilename.data() + numEnd, bundleNumber);
113 if (parsed.ec != std::errc{} ||
114 parsed.ptr != originalFilename.data() + numEnd) {
115 continue;
116 }
117 if (bundleNumber != number) {
118 continue;
119 }
120
121 std::string baseName = originalFilename.substr(0, upos);
122 // Drop the "_<n>" token and keep everything after it, so a compound
123 // extension (".con.gz", ".con.zst") survives whichever side of the
124 // underscore it sits on.
125 size_t extStart =
126 originalFilename.find_first_not_of("0123456789", upos + 1);
127 if (extStart == std::string::npos) {
128 extStart = originalFilename.size();
129 }
130 std::string newFilename = baseName + originalFilename.substr(extStart);
131
132 try {
133 fs::copy_file(originalFilename, newFilename,
134 fs::copy_options::overwrite_existing);
135 filenames.push_back(newFilename);
136 } catch (const fs::filesystem_error &e) {
137 EONC_LOG_ERROR("unbundle: problem copying {} to {}: {}", originalFilename,
138 newFilename, e.what());
139 }
140 }
141
142 return filenames;
143}
144
145void deleteUnbundledFiles(const std::vector<std::string> &unbundledFilenames) {
146 for (const auto &filename : unbundledFilenames) {
147 std::error_code ec;
148 fs::remove(filename, ec);
149 if (ec) {
150 EONC_LOG_ERROR("deleteUnbundledFiles: cannot remove {}: {}", filename,
151 ec.message());
152 }
153 }
154}
155
156namespace {
157
162std::pair<std::string, std::string>
163splitBundleExtension(const std::string &name) {
164 const size_t pos = name.find_last_of('.');
165 if (pos == std::string::npos) {
166 return {name, ""};
167 }
168 const std::string tail = name.substr(pos);
169 if (pos > 0 && (tail == ".gz" || tail == ".zst")) {
170 const size_t inner = name.find_last_of('.', pos - 1);
171 if (inner != std::string::npos) {
172 return {name.substr(0, inner), name.substr(inner)};
173 }
174 }
175 return {name.substr(0, pos), tail};
176}
177
178} // namespace
179
180void bundle(int number, const std::vector<std::string> &filenames,
181 std::vector<std::string> *bundledFilenames) {
182 for (const auto &filename : filenames) {
183 const auto [baseName, ext] = splitBundleExtension(filename);
184 const std::string newFilename =
185 std::format("{}_{}{}", baseName, number, ext);
186
187 try {
188 fs::rename(filename, newFilename);
189 bundledFilenames->push_back(newFilename);
190 } catch (const fs::filesystem_error &e) {
191 EONC_LOG_ERROR("bundle: cannot rename {} to {}: {}", filename,
192 newFilename, e.what());
193 }
194 }
195}
196
197} // namespace eonc
#define EONC_LOG_ERROR(...)
Definition EonLogger.h:261
RAII resource manager for the ARTn C library with global synchronization.
void bundle(int number, const std::vector< std::string > &filenames, std::vector< std::string > *bundledFilenames)
Definition Bundling.cpp:180
void deleteUnbundledFiles(const std::vector< std::string > &unbundledFilenames)
Definition Bundling.cpp:145
std::vector< std::string > unbundle(int number)
Definition Bundling.cpp:71
int getBundleSize()
Definition Bundling.cpp:27