NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
archive.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 NumeRe: Framework fuer Numerische Rechnungen
3 Copyright (C) 2023 Erik Haenel et al.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17******************************************************************************/
18
19#include "archive.hpp"
20#include "stringtools.hpp"
21#include "../io/logger.hpp"
22
23#include <wx/zipstrm.h>
24#include <wx/tarstrm.h>
25#include <wx/zstream.h>
26#include <wx/wfstream.h>
27#include <wx/stream.h>
28#include <wx/dir.h>
29
30#include <fstream>
31#include <cstring>
32#include <memory>
33
34#include "../../kernel.hpp"
35
36namespace Archive
37{
48 Type detectType(const std::string& sArchiveFileName)
49 {
50 std::ifstream file(sArchiveFileName, std::ios_base::binary);
51
52 static const std::string ZIPHEADER("\x50\x4b\x03\x04");
53 static const std::string GZHEADER("\x1f\x8b\x08");
54 static const std::string TARMAGIC("ustar");
55 std::string sExt;
56
57 // Extract extension
58 if (sArchiveFileName.find('.') != std::string::npos)
59 sExt = toLowerCase(sArchiveFileName.substr(sArchiveFileName.rfind('.')));
60
61 // If file does not already exist, determine the archive
62 // type only upon the file extension
63 if (!file.good() || file.eof())
64 {
65 if (sExt == ".zip" || sExt == ".xlsx" || sExt == ".ods")
66 return ARCHIVE_ZIP;
67 else if (sExt == ".tar")
68 return ARCHIVE_TAR;
69 else if (sExt == ".gz")
70 return ARCHIVE_GZ;
71 else if (sExt == ".zz")
72 return ARCHIVE_ZLIB;
73
74 return ARCHIVE_NONE;
75 }
76
77 char magicNumber[6] = {0,0,0,0,0,0};
78
79 file.read(magicNumber, 4);
80 g_logger.debug("magicNumber = '" + std::string(magicNumber) + "'");
81
82 // Test for zip and gzip
83 if (magicNumber == ZIPHEADER)
84 return ARCHIVE_ZIP;
85 else if (GZHEADER == std::string(magicNumber).substr(0, 3))
86 return ARCHIVE_GZ;
87
88 // try to detect TAR
89 file.seekg(257, std::ios_base::beg);
90 file.read(magicNumber, TARMAGIC.length());
91 g_logger.debug("magicNumber = '" + std::string(magicNumber) + "'");
92
93 if (magicNumber == TARMAGIC)
94 return ARCHIVE_TAR;
95
96 return ARCHIVE_NONE;
97 }
98
99
109 static std::string getGZipFileName(const std::string& sArchiveFileName)
110 {
111 std::ifstream gzip(sArchiveFileName, std::ios_base::binary);
112
113 if (!gzip.good() || gzip.eof())
114 return "";
115
116 // Search for the GZIP flags
117 gzip.seekg(3, std::ios_base::beg);
118 std::uint8_t flg;
119 gzip >> flg;
120
121 // Does it contain a file name?
122 if (flg & 8)
123 {
124 gzip.seekg(10, std::ios_base::beg);
125
126 // Extra fields may be possible and are located
127 // before the file name
128 if (flg & 4)
129 {
130 uint16_t xlen;
131 gzip >> xlen;
132 gzip.seekg(xlen, std::ios_base::cur);
133 }
134
135 std::string fileName;
136 char c;
137 gzip >> c;
138
139 // Read until we reach a null character
140 while (c != 0)
141 {
142 fileName += c;
143 gzip >> c;
144 }
145
146 return fileName;
147 }
148
149 return "";
150 }
151
152
168 void pack(const std::vector<std::string>& vFileList, const std::string& sTargetFile, Type type)
169 {
170 if (type == ARCHIVE_AUTO)
171 type = detectType(sTargetFile);
172
173 if (type == ARCHIVE_NONE)
174 throw SyntaxError(SyntaxError::INVALID_FILETYPE, sTargetFile, sTargetFile.substr(sTargetFile.rfind('.')), sTargetFile);
175
176 const Settings& _option = NumeReKernel::getInstance()->getSettings();
177
178 if (type == ARCHIVE_ZIP)
179 {
180 wxFFileOutputStream out(sTargetFile);
181 wxZipOutputStream outzip(out, 6);
182
183 for (size_t i = 0; i < vFileList.size(); i++)
184 {
185 if (!fileExists(vFileList[i]))
186 {
187 // Handle the recursion
188 g_logger.debug("Including directory: " + vFileList[i]);
189 std::string sDirectory = vFileList[i] + "/*";
190 std::vector<std::string> vFiles = getFileList(sDirectory, _option, 1);
191
192 while (vFiles.size() || getFolderList(sDirectory, _option).size() > 2)
193 {
194 for (size_t j = 0; j < vFiles.size(); j++)
195 {
196 // Files are simple packed together without additional paths
197 g_logger.debug("Including file: " + vFiles[j]);
198 wxFFileInputStream file(vFiles[j]);
199 wxZipEntry* newEntry = new wxZipEntry(vFiles[j].substr(vFileList[i].find_last_of("/\\")+1));
200
201 outzip.PutNextEntry(newEntry);
202 outzip.Write(file);
203 outzip.CloseEntry();
204 }
205
206 sDirectory += "/*";
207 vFiles = getFileList(sDirectory, _option, 1);
208 }
209 }
210 else
211 {
212 // Files are simple packed together without additional paths
213 g_logger.debug("Including file: " + vFileList[i]);
214 wxFFileInputStream file(vFileList[i]);
215 wxZipEntry* newEntry = new wxZipEntry(vFileList[i].substr(vFileList[i].find_last_of("/\\")+1));
216
217 outzip.PutNextEntry(newEntry);
218 outzip.Write(file);
219 outzip.CloseEntry();
220 }
221 }
222 }
223 else if (type == ARCHIVE_TAR)
224 {
225 wxFFileOutputStream out(sTargetFile);
226 wxTarOutputStream outtar(out);
227
228 for (size_t i = 0; i < vFileList.size(); i++)
229 {
230 if (!fileExists(vFileList[i]))
231 {
232 // Handle the recursion
233 g_logger.debug("Including directory: " + vFileList[i]);
234 std::string sDirectory = vFileList[i] + "/*";
235 std::vector<std::string> vFiles = getFileList(sDirectory, _option, 1);
236
237 while (vFiles.size() || getFolderList(sDirectory, _option).size() > 2)
238 {
239 for (size_t j = 0; j < vFiles.size(); j++)
240 {
241 // Files are simple packed together without additional paths
242 g_logger.debug("Including file: " + vFiles[j]);
243 wxFFileInputStream file(vFiles[j]);
244 wxTarEntry* newEntry = new wxTarEntry(vFiles[j].substr(vFileList[i].find_last_of("/\\")+1));
245
246 outtar.PutNextEntry(newEntry);
247 outtar.Write(file);
248 outtar.CloseEntry();
249 }
250
251 sDirectory += "/*";
252 vFiles = getFileList(sDirectory, _option, 1);
253 }
254 }
255 else
256 {
257 // Files are simple packed together without additional paths
258 g_logger.debug("Including file: " + vFileList[i]);
259 wxFFileInputStream file(vFileList[i]);
260 wxTarEntry* newEntry = new wxTarEntry(vFileList[i].substr(vFileList[i].find_last_of("/\\")+1));
261
262 outtar.PutNextEntry(newEntry);
263 outtar.Write(file);
264 outtar.CloseEntry();
265 }
266 }
267 }
268 else if (type == ARCHIVE_GZ)
269 {
270 std::string sFile = vFileList.front();
271 bool tempTar = false;
272
273 // Multiple files have to be tar'ed first
274 if (vFileList.size() > 1 || !fileExists(sFile))
275 {
276 sFile = sTargetFile.substr(0, sTargetFile.rfind('.')) + ".tar";
277 tempTar = true;
278 pack(vFileList, sFile, ARCHIVE_TAR);
279 }
280
281 wxFFileOutputStream out(sTargetFile);
282 wxZlibOutputStream outzlib(out, -1, wxZLIB_GZIP);
283
284 g_logger.debug("Including file: " + sFile);
285 wxFFileInputStream file(sFile);
286 outzlib.Write(file);
287
288 if (tempTar)
289 remove(sFile.c_str());
290 }
291 }
292
293
307 std::vector<std::string> unpack(const std::string& sArchiveName, const std::string& sTargetPath)
308 {
309 if (!fileExists(sArchiveName))
310 throw SyntaxError(SyntaxError::FILE_NOT_EXIST, sArchiveName, sArchiveName, sArchiveName);
311
312 Type type = detectType(sArchiveName);
313
314 if (type == ARCHIVE_NONE)
315 throw SyntaxError(SyntaxError::INVALID_FILETYPE, sArchiveName, sArchiveName.substr(sArchiveName.rfind('.')), sArchiveName);
316
317 if (type == ARCHIVE_ZIP)
318 g_logger.debug("ZIP detected.");
319 else if (type == ARCHIVE_TAR)
320 g_logger.debug("TAR detected.");
321 else if (type == ARCHIVE_GZ)
322 g_logger.debug("GZIP detected.");
323 else if (type == ARCHIVE_ZLIB)
324 g_logger.debug("ZLIB detected.");
325
327 std::vector<std::string> vFiles;
328
329 if (type == ARCHIVE_ZIP)
330 {
331 wxFFileInputStream in(sArchiveName);
332 wxZipInputStream zip(in);
333 std::unique_ptr<wxZipEntry> entry;
334
335 while (entry.reset(zip.GetNextEntry()), entry.get() != nullptr)
336 {
337 std::string entryName = replacePathSeparator(entry->GetName().ToStdString());
338
339 if (sTargetPath.length())
340 {
341 entryName = _fSys.ValidizeAndPrepareName(sTargetPath + "/" + entryName, "");
342
343 g_logger.debug("Entry name: " + entryName);
344 vFiles.push_back(entryName);
345 wxFileOutputStream stream(sTargetPath + "/" + entry->GetName());
346 zip.Read(stream);
347 }
348 else
349 vFiles.push_back(entryName);
350 }
351 }
352 else if (type == ARCHIVE_TAR)
353 {
354 wxFFileInputStream in(sArchiveName);
355 wxTarInputStream tar(in);
356 std::unique_ptr<wxTarEntry> entry;
357
358 while (entry.reset(tar.GetNextEntry()), entry.get() != nullptr)
359 {
360 if (entry->IsDir())
361 continue;
362
363 std::string entryName = replacePathSeparator(entry->GetName().ToStdString());
364
365 if (sTargetPath.length())
366 {
367 entryName = _fSys.ValidizeAndPrepareName(sTargetPath + "/" + entryName, "");
368
369 g_logger.debug("Entry name: " + entryName);
370 vFiles.push_back(entryName);
371 wxFileOutputStream stream(sTargetPath + "/" + entry->GetName());
372 tar.Read(stream);
373 }
374 else
375 vFiles.push_back(entryName);
376 }
377 }
378 else if (type == ARCHIVE_GZ || type == ARCHIVE_ZLIB)
379 {
380 wxFFileInputStream in(sArchiveName);
381 wxZlibInputStream zlib(in);
382
383 std::string sUnpackedName = getGZipFileName(sArchiveName);
384
385 if (sTargetPath.length())
386 {
387 if (!sUnpackedName.length())
388 {
389 sUnpackedName = sArchiveName.substr(0, sArchiveName.rfind('.'))+".tar";
390 sUnpackedName = _fSys.ValidizeAndPrepareName(sTargetPath + "/" + sUnpackedName.substr(sUnpackedName.rfind('/')+1), "");
391 }
392 else
393 sUnpackedName = _fSys.ValidizeAndPrepareName(sTargetPath + "/" + sUnpackedName, "");
394
395 g_logger.debug("Entry name: " + sUnpackedName);
396 vFiles.push_back(sUnpackedName);
397
398 wxFileOutputStream stream(sUnpackedName);
399 zlib.Read(stream);
400 }
401 else
402 vFiles.push_back(sUnpackedName);
403 }
404
405 return vFiles;
406 }
407}
408
409
std::string toLowerCase(const std::string &)
Converts uppercase to lowercase letters.
void debug(const std::string &sMessage)
Convenience member function.
Definition: logger.hpp:94
This class implements the basic input/ output file system and provides functionalities to work with f...
Definition: filesystem.hpp:92
std::string ValidizeAndPrepareName(const std::string &_sFileName, const std::string &sExtension=".dat") const
This member function validizes the passed file name and creates the needed folders on-the-fly.
Definition: filesystem.cpp:424
static NumeReKernel * getInstance()
This static member function returns a a pointer to the singleton instance of the kernel.
Definition: kernel.hpp:221
FileSystem & getFileSystem()
Definition: kernel.hpp:258
Settings & getSettings()
Definition: kernel.hpp:296
This class manages the setting values of the internal (kernel) settings of this application.
Definition: settings.hpp:663
Common exception class for all exceptions thrown in NumeRe.
Definition: error.hpp:32
@ INVALID_FILETYPE
Definition: error.hpp:127
@ FILE_NOT_EXIST
Definition: error.hpp:105
std::string replacePathSeparator(const std::string &)
This function replaces the Windows style path sparators to UNIX style.
bool fileExists(const string &)
This function checks, whether the file with the passed file name exists.
Definition: tools.cpp:2500
DetachedLogger g_logger
Definition: logger.cpp:23
std::vector< std::string > unpack(const std::string &sArchiveName, const std::string &sTargetPath)
Unpacks an archive file format into its folder structure at the specified location....
Definition: archive.cpp:307
static std::string getGZipFileName(const std::string &sArchiveFileName)
Static helper function to read a possible available file name in the GZIP header section.
Definition: archive.cpp:109
Type detectType(const std::string &sArchiveFileName)
Detects the type of the archive based upon the magic numbers in their header sections or upon the fil...
Definition: archive.cpp:48
void pack(const std::vector< std::string > &vFileList, const std::string &sTargetFile, Type type)
Pack a set of files or folders into an archive file type with the specified file name....
Definition: archive.cpp:168
@ ARCHIVE_ZLIB
Definition: archive.hpp:32
@ ARCHIVE_GZ
Definition: archive.hpp:31
@ ARCHIVE_TAR
Definition: archive.hpp:30
@ ARCHIVE_AUTO
Definition: archive.hpp:29
@ ARCHIVE_NONE
Definition: archive.hpp:34
@ ARCHIVE_ZIP
Definition: archive.hpp:33
vector< string > getFileList(const string &sDirectory, const Settings &_option, int nFlags)
This function returns a list of files (including their paths, if nFlags & 1).
Definition: tools.cpp:2853
vector< string > getFolderList(const string &sDirectory, const Settings &_option, int nFlags)
This function returns a list of directories (including their paths, if nFlags & 1).
Definition: tools.cpp:2925