C++ Utilities 5.26.1
Useful C++ classes and routines such as argument parser, IO and conversion utilities
Loading...
Searching...
No Matches
archive.cpp
Go to the documentation of this file.
1#include "./archive.h"
2
4#include "../io/misc.h"
5
6#include <archive.h>
7#include <archive_entry.h>
8
9#include <filesystem>
10#include <utility>
11
12using namespace CppUtilities;
13
14namespace CppUtilities {
15
22
25struct AddDirectoryToFileMap {
26 bool operator()(std::string_view path)
27 {
28 fileMap[std::string(path)];
29 return false;
30 }
31 FileMap &fileMap;
32};
33
34struct AddFileToFileMap {
35 bool operator()(std::string_view directoryPath, ArchiveFile &&file)
36 {
37 fileMap[std::string(directoryPath)].emplace_back(std::move(file));
38 return false;
39 }
40 FileMap &fileMap;
41};
42
43struct ArchiveHandle {
44 explicit ArchiveHandle()
45 : handle(archive_read_new())
46 {
47 }
48
49 ~ArchiveHandle()
50 {
51 if (handle) {
52 archive_read_free(handle);
53 }
54 }
55
56 ArchiveHandle(const ArchiveHandle &) = delete;
57 ArchiveHandle(ArchiveHandle &&) = delete;
58
59 void close(std::string_view archiveName, std::string_view errorMessage)
60 {
61 if (!handle) {
62 return;
63 }
64 if (const auto returnCode = archive_read_free(std::exchange(handle, nullptr)); returnCode != ARCHIVE_OK) {
65 throw ArchiveException(errorMessage.empty() ? argsToString("Unable to free archive \"", archiveName, '\"')
66 : argsToString("Unable to free archive \"", archiveName, "\" after error: ", errorMessage));
67 }
68 }
69
70 operator struct archive *()
71 {
72 return handle;
73 }
74
75 struct archive *handle;
76};
77
78void walkThroughArchiveInternal(ArchiveHandle &ar, std::string_view archiveName, const FilePredicate &isFileRelevant, FileHandler &&fileHandler,
79 DirectoryHandler &&directoryHandler)
80{
81 // iterate through all archive entries
82 struct archive_entry *const entry = archive_entry_new();
83 auto fileContent = std::string();
84 while (archive_read_next_header2(ar, entry) == ARCHIVE_OK) {
85 // check entry type (only dirs, files and symlinks relevant here)
86 const auto entryType(archive_entry_filetype(entry));
87 if (entryType != AE_IFDIR && entryType != AE_IFREG && entryType != AE_IFLNK) {
88 continue;
89 }
90
91 // get file path
92 const char *filePath = archive_entry_pathname_utf8(entry);
93 if (!filePath) {
94 filePath = archive_entry_pathname(entry);
95 }
96 if (!filePath) {
97 continue;
98 }
99
100 // get permissions
101 const mode_t perm = archive_entry_perm(entry);
102
103 // add directories explicitly to get the entire tree though skipping irrelevant files
104 if (entryType == AE_IFDIR) {
105 if (!directoryHandler) {
106 continue;
107 }
108 // remove trailing slashes
109 const char *dirEnd = filePath;
110 for (const char *i = filePath; *i; ++i) {
111 if (*i != '/') {
112 dirEnd = i + 1;
113 }
114 }
115 if (directoryHandler(std::string_view(filePath, static_cast<std::size_t>(dirEnd - filePath)))) {
116 goto free;
117 }
118 continue;
119 }
120
121 // split the path into dir and fileName
122 const char *fileName = filePath, *dirEnd = filePath;
123 for (const char *i = filePath; *i; ++i) {
124 if (*i == '/') {
125 fileName = i + 1;
126 dirEnd = i;
127 }
128 }
129
130 // prevent looking into irrelevant files
131 if (isFileRelevant && !isFileRelevant(filePath, fileName, perm)) {
132 continue;
133 }
134
135 // read timestamps
136 const auto creationTime = DateTime::fromTimeStampGmt(archive_entry_ctime(entry));
137 const auto modificationTime = DateTime::fromTimeStampGmt(archive_entry_mtime(entry));
138
139 // read symlink
140 if (entryType == AE_IFLNK) {
141 if (fileHandler(std::string_view(filePath, static_cast<std::string::size_type>(dirEnd - filePath)),
142 ArchiveFile(fileName, std::string(archive_entry_symlink_utf8(entry)), ArchiveFileType::Link, creationTime, modificationTime))) {
143 goto free;
144 }
145 continue;
146 }
147
148 // determine file size to pre-allocate buffer for file content
149 const la_int64_t fileSize = archive_entry_size(entry);
150 fileContent.clear();
151 if (fileSize > 0) {
152 fileContent.reserve(static_cast<std::string::size_type>(fileSize));
153 }
154
155 // read file content
156 const char *buff;
157 auto size = std::size_t();
158 auto offset = la_int64_t();
159 for (;;) {
160 const auto returnCode = archive_read_data_block(ar, reinterpret_cast<const void **>(&buff), &size, &offset);
161 if (returnCode == ARCHIVE_EOF || returnCode < ARCHIVE_OK) {
162 break;
163 }
164 fileContent.append(buff, size);
165 }
166
167 // move it to results
168 if (fileHandler(std::string_view(filePath, static_cast<std::string::size_type>(dirEnd - filePath)),
169 ArchiveFile(fileName, std::move(fileContent), ArchiveFileType::Regular, creationTime, modificationTime))) {
170 goto free;
171 }
172 }
173
174free:
175 // check for errors
176 const auto *const archiveError = archive_error_string(ar);
177 const auto errorMessage = archiveError ? std::string(archiveError) : std::string();
178
179 // free resources used by libarchive
180 archive_entry_free(entry);
181 ar.close(archiveName, errorMessage);
182 if (archiveError) {
183 throw ArchiveException(argsToString("An error occurred when reading archive \"", archiveName, "\": ", errorMessage));
184 }
185}
186
188
192void walkThroughArchiveFromBuffer(std::string_view archiveData, std::string_view archiveName, const FilePredicate &isFileRelevant,
193 FileHandler &&fileHandler, DirectoryHandler &&directoryHandler)
194{
195 // refuse opening empty buffer
196 if (archiveData.empty()) {
197 throw ArchiveException("Unable to open archive \"" % archiveName + "\": archive data is empty");
198 }
199 // open archive buffer using libarchive
200 auto ar = ArchiveHandle();
201 archive_read_support_filter_all(ar);
202 archive_read_support_format_all(ar);
203 const auto returnCode = archive_read_open_memory(ar, archiveData.data(), archiveData.size());
204 if (returnCode != ARCHIVE_OK) {
205 if (const char *const error = archive_error_string(ar)) {
206 throw ArchiveException("Unable to open/read archive \"" % archiveName % "\": " + error);
207 } else {
208 throw ArchiveException("Unable to open/read archive \"" % archiveName + "\": unable to open archive from memory");
209 }
210 }
211 walkThroughArchiveInternal(ar, archiveName, isFileRelevant, std::move(fileHandler), std::move(directoryHandler));
212}
213
217FileMap extractFilesFromBuffer(std::string_view archiveData, std::string_view archiveName, const FilePredicate &isFileRelevant)
218{
219 auto results = FileMap();
220 walkThroughArchiveFromBuffer(archiveData, archiveName, isFileRelevant, AddFileToFileMap{ results }, AddDirectoryToFileMap{ results });
221 return results;
222}
223
228 std::string_view archivePath, const FilePredicate &isFileRelevant, FileHandler &&fileHandler, DirectoryHandler &&directoryHandler)
229{
230 // open archive file using libarchive
231 if (archivePath.empty()) {
232 throw ArchiveException("Unable to open archive: no path specified");
233 }
234 auto ec = std::error_code();
235 auto size = std::filesystem::file_size(archivePath, ec);
236 if (ec) {
237 throw ArchiveException("Unable to determine size of \"" % archivePath % "\": " + ec.message());
238 }
239 if (!size) {
240 throw ArchiveException("Unable to open archive \"" % archivePath + "\": file is empty");
241 }
242 auto ar = ArchiveHandle();
243 archive_read_support_filter_all(ar);
244 archive_read_support_format_all(ar);
245 const auto returnCode = archive_read_open_filename(ar, archivePath.data(), 10240);
246 if (returnCode != ARCHIVE_OK) {
247 if (const char *const error = archive_error_string(ar)) {
248 throw ArchiveException("Unable to open/read archive \"" % archivePath % "\": " + error);
249 } else {
250 throw ArchiveException("Unable to open/read archive \"" % archivePath + "\": unable to open archive from file");
251 }
252 }
253 walkThroughArchiveInternal(ar, archivePath, isFileRelevant, std::move(fileHandler), std::move(directoryHandler));
254}
255
259FileMap extractFiles(std::string_view archivePath, const FilePredicate &isFileRelevant)
260{
261 auto results = FileMap();
262 walkThroughArchive(archivePath, isFileRelevant, AddFileToFileMap{ results }, AddDirectoryToFileMap{ results });
263 return results;
264}
265
266} // namespace CppUtilities
The ArchiveException class is thrown by the various archiving-related functions of this library when ...
Definition archive.h:21
~ArchiveException() override
Destroys the ArchiveException.
Definition archive.cpp:19
static constexpr DateTime fromTimeStampGmt(std::time_t timeStamp)
Constructs a new DateTime object with the GMT time from the specified UNIX timeStamp.
Definition datetime.h:274
Contains all utilities provides by the c++utilities library.
CPP_UTILITIES_EXPORT void walkThroughArchiveFromBuffer(std::string_view archiveData, std::string_view archiveName, const FilePredicate &isFileRelevant=FilePredicate(), FileHandler &&fileHandler=FileHandler(), DirectoryHandler &&directoryHandler=DirectoryHandler())
Invokes callbacks for files and directories in the specified archive.
Definition archive.cpp:192
CPP_UTILITIES_EXPORT FileMap extractFilesFromBuffer(std::string_view archiveData, std::string_view archiveName, const FilePredicate &isFileRelevant=FilePredicate())
Extracts the specified archive.
Definition archive.cpp:217
CPP_UTILITIES_EXPORT void walkThroughArchive(std::string_view archivePath, const FilePredicate &isFileRelevant=FilePredicate(), FileHandler &&fileHandler=FileHandler(), DirectoryHandler &&directoryHandler=DirectoryHandler())
Invokes callbacks for files and directories in the specified archive.
Definition archive.cpp:227
std::function< bool(const char *, const char *, mode_t)> FilePredicate
A function that is invoked for each file within an archive. If it returns true, the file is considere...
Definition archive.h:72
CPP_UTILITIES_EXPORT FileMap extractFiles(std::string_view archivePath, const FilePredicate &isFileRelevant=FilePredicate())
Extracts the specified archive.
Definition archive.cpp:259
std::function< bool(std::string_view path)> DirectoryHandler
A function that is invoked by the walk-through-functions to return a directory.
Definition archive.h:74
std::map< std::string, std::vector< ArchiveFile > > FileMap
A map of files extracted from an archive. Keys represent directories and values files within those di...
Definition archive.h:70
StringType argsToString(Args &&...args)
std::function< bool(std::string_view path, ArchiveFile &&file)> FileHandler
A function that is invoked by the walk-through-functions to return a file.
Definition archive.h:76
CPP_UTILITIES_EXPORT std::string fileName(const std::string &path)
Returns the file name and extension of the specified path string.
Definition path.cpp:17
STL namespace.
The ArchiveFile class holds data about a file within an archive.
Definition archive.h:52
constexpr int i