Adds initial intermediate qt6 porting state
This commit is contained in:
11247 files changed
+1891537
No files matched your search
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# CMake Script
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
FIND_PACKAGE(BZip2 REQUIRED)
|
||||
SET(nglcomp_SRCS
|
||||
nglcomp_bzip2iodevice.cpp
|
||||
nglcomp_bzip2iodevice.h
|
||||
nglcomp_compress.cpp
|
||||
nglcomp_compress.h
|
||||
nglcomp_export.h
|
||||
nglcomp_tarfilter.cpp
|
||||
nglcomp_tarfilter.h
|
||||
)
|
||||
NGL_ADD_LIBRARY(WS nglcompression SHARED ${nglcomp_SRCS})
|
||||
NGL_TARGET_LINK_LIBRARIES(nglcompression
|
||||
PUBLIC
|
||||
Qt5::Core
|
||||
PRIVATE
|
||||
Qt5::Concurrent
|
||||
NGLLogger
|
||||
${BZIP2_LIBRARIES}
|
||||
# BZip2::BZip2 # not available with cmake 3.5
|
||||
)
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(nglcompression
|
||||
PRIVATE
|
||||
${BZIP2_INCLUDE_DIR}
|
||||
)
|
||||
NGL_INSTALL_TARGET(nglcompression)
|
||||
|
||||
IF(BUILD_WS AND BUILD_TESTING)
|
||||
REMOVE_DEFINITIONS(-DQT_GUI_LIB)
|
||||
ADD_EXECUTABLE(nglcomp_test nglcomp_test.cpp nglcomp_test.h)
|
||||
|
||||
TARGET_LINK_LIBRARIES(nglcomp_test
|
||||
NGLLogger
|
||||
nglcoreapplication
|
||||
ngltools
|
||||
nglcompression
|
||||
Qt5::Test
|
||||
)
|
||||
|
||||
ADD_TEST(NAME nglcomp_test COMMAND $<TARGET_FILE:nglcomp_test>)
|
||||
ENDIF()
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "nglcomp_bzip2iodevice.h"
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QString>
|
||||
|
||||
#include <bzlib.h>
|
||||
|
||||
|
||||
|
||||
|
||||
class BZip2IODevice::Private
|
||||
{
|
||||
public:
|
||||
Private(BZip2IODevice *parent_, QIODevice *dev, int bs)
|
||||
: parent(parent_)
|
||||
, device(dev)
|
||||
, iCurPos(0)
|
||||
, iMaxBufSize(1024 * 1024)
|
||||
, blocksize(bs)
|
||||
, initialized(false)
|
||||
, decomressStreamEnd(false)
|
||||
{
|
||||
buffer.resize(iMaxBufSize);
|
||||
}
|
||||
|
||||
bool initializeCompress();
|
||||
qint64 doCompress(const char *in, qint64 iMaxLen);
|
||||
bool finishCompress();
|
||||
bool initializeDecompress();
|
||||
qint64 doDecompress(char *out, qint64 iMaxLen);
|
||||
bool finishDecompress();
|
||||
static QString bzipError2String(int errCode);
|
||||
|
||||
QAtomicInt aborted { 0 };
|
||||
BZip2IODevice *parent;
|
||||
QIODevice *device;
|
||||
unsigned int iCurPos;
|
||||
unsigned int iMaxBufSize;
|
||||
unsigned int blocksize;
|
||||
bool initialized;
|
||||
bool decomressStreamEnd;
|
||||
QByteArray buffer;
|
||||
bz_stream stream;
|
||||
};
|
||||
|
||||
bool BZip2IODevice::Private::initializeCompress()
|
||||
{
|
||||
if (initialized)
|
||||
return true;
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
int blockSize = (blocksize > 0 && blocksize < 10) ? blocksize : 10;
|
||||
int ret = BZ2_bzCompressInit(&stream, blockSize, 0, 0);
|
||||
if (ret != BZ_OK)
|
||||
{
|
||||
parent->setErrorString(QStringLiteral("Error initializing bzip2 %1").arg(bzipError2String(ret)));
|
||||
initialized = false;
|
||||
}
|
||||
else
|
||||
initialized = true;
|
||||
|
||||
return initialized;
|
||||
}
|
||||
|
||||
qint64 BZip2IODevice::Private::doCompress(const char *in, qint64 iMaxLen)
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
if (parent->errorString().isEmpty())
|
||||
parent->setErrorString(QLatin1String("Internal Error - bzip not initialized"));
|
||||
finishCompress();
|
||||
return -1;
|
||||
}
|
||||
if (iMaxLen > 2 * 1024 * 1024 * 1024LL)
|
||||
{
|
||||
finishCompress();
|
||||
parent->setErrorString(QLatin1String("Currently can't handle more than 2GB!"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream.next_in = const_cast<char *>(in);
|
||||
stream.avail_in = iMaxLen;
|
||||
|
||||
do
|
||||
{
|
||||
if (aborted) return 0;
|
||||
|
||||
stream.next_out = buffer.data();
|
||||
stream.avail_out = buffer.size();
|
||||
|
||||
const int ret = BZ2_bzCompress(&stream, BZ_RUN);
|
||||
if (ret != BZ_RUN_OK)
|
||||
{
|
||||
finishCompress();
|
||||
parent->setErrorString(QStringLiteral("Error executing BZ2_bzCompress: %1").arg(bzipError2String(ret)));
|
||||
return -1;
|
||||
}
|
||||
int iBytesToWrite = buffer.size() - stream.avail_out;
|
||||
if (iBytesToWrite)
|
||||
{
|
||||
if (device->write(buffer.constData(), iBytesToWrite) != iBytesToWrite)
|
||||
{
|
||||
finishCompress();
|
||||
parent->setErrorString(QLatin1String("Error writing to QIODevice"));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (stream.avail_in != 0);
|
||||
|
||||
return stream.next_in - in;
|
||||
}
|
||||
|
||||
bool BZip2IODevice::Private::finishCompress()
|
||||
{
|
||||
if (!initialized || aborted)
|
||||
return true;
|
||||
|
||||
int ret = -1;
|
||||
|
||||
do
|
||||
{
|
||||
stream.next_out = buffer.data();
|
||||
stream.avail_out = buffer.size();
|
||||
ret = BZ2_bzCompress(&stream, BZ_FINISH);
|
||||
if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END)
|
||||
{
|
||||
finishCompress();
|
||||
parent->setErrorString(QStringLiteral("Error executing BZ2_bzCompress: %1").arg(bzipError2String(ret)));
|
||||
return false;
|
||||
}
|
||||
int iBytesToWrite = buffer.size() - stream.avail_out;
|
||||
if (device->write(buffer.constData(), iBytesToWrite) != iBytesToWrite)
|
||||
{
|
||||
finishCompress();
|
||||
parent->setErrorString(QLatin1String("Error writing to QIODevice"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (ret != BZ_STREAM_END);
|
||||
BZ2_bzCompressEnd(&stream);
|
||||
|
||||
initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BZip2IODevice::Private::initializeDecompress()
|
||||
{
|
||||
if (initialized)
|
||||
return true;
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
int ret = BZ2_bzDecompressInit(&stream, 0, 0);
|
||||
if (ret != BZ_OK)
|
||||
{
|
||||
parent->setErrorString(QStringLiteral("Error initializing bzip2: %1").arg(bzipError2String(ret)));
|
||||
initialized = false;
|
||||
}
|
||||
else
|
||||
initialized = true;
|
||||
decomressStreamEnd = false;
|
||||
return initialized;
|
||||
}
|
||||
|
||||
qint64 BZip2IODevice::Private::doDecompress(char *out, qint64 iMaxLen)
|
||||
{
|
||||
int ret = -1;
|
||||
if (!initialized)
|
||||
{
|
||||
if (parent->errorString().isEmpty())
|
||||
parent->setErrorString(QLatin1String("Internal Error - bzip not initialized"));
|
||||
finishDecompress();
|
||||
return -1;
|
||||
}
|
||||
if (decomressStreamEnd)
|
||||
return 0;
|
||||
|
||||
if (iMaxLen > 2 * 1024 * 1024 * 1024LL)
|
||||
{
|
||||
finishCompress();
|
||||
parent->setErrorString(QLatin1String("Currently can't handle more than 2GB!"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream.next_out = out;
|
||||
stream.avail_out = iMaxLen;
|
||||
|
||||
do
|
||||
{
|
||||
if (ret != BZ_STREAM_END && stream.avail_in == 0)
|
||||
{
|
||||
qint64 iReadCnt = device->read(buffer.data(), buffer.size());
|
||||
if (iReadCnt <= 0)
|
||||
return -1;
|
||||
stream.next_in = buffer.data();
|
||||
stream.avail_in = iReadCnt;
|
||||
}
|
||||
|
||||
ret = BZ2_bzDecompress(&stream);
|
||||
if (ret != BZ_OK && ret != BZ_STREAM_END)
|
||||
{
|
||||
parent->setErrorString(QStringLiteral("Error executing BZ2_bzCompress: %1").arg(bzipError2String(ret)));
|
||||
finishDecompress();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
while (stream.avail_out != 0 && ret != BZ_STREAM_END);
|
||||
decomressStreamEnd = (ret == BZ_STREAM_END);
|
||||
return stream.next_out - out;
|
||||
}
|
||||
|
||||
bool BZip2IODevice::Private::finishDecompress()
|
||||
{
|
||||
if (!initialized)
|
||||
return true;
|
||||
BZ2_bzDecompressEnd(&stream);
|
||||
|
||||
initialized = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
QString BZip2IODevice::Private::bzipError2String(int errCode)
|
||||
{
|
||||
switch (errCode)
|
||||
{
|
||||
case BZ_SEQUENCE_ERROR:
|
||||
return QLatin1String("Sequence error");
|
||||
case BZ_PARAM_ERROR:
|
||||
return QLatin1String("Parameter not correct");
|
||||
case BZ_MEM_ERROR:
|
||||
return QLatin1String("Memory allocation error");
|
||||
case BZ_DATA_ERROR:
|
||||
return QLatin1String("Invalid input data");
|
||||
case BZ_DATA_ERROR_MAGIC:
|
||||
return QLatin1String("Not a bzip2 file");
|
||||
case BZ_IO_ERROR:
|
||||
return QLatin1String("I/O error");
|
||||
case BZ_UNEXPECTED_EOF:
|
||||
return QLatin1String("Unexpected end of file");
|
||||
case BZ_OUTBUFF_FULL:
|
||||
return QLatin1String("Output buffer full");
|
||||
case BZ_CONFIG_ERROR:
|
||||
return QLatin1String("Wrong config parameter");
|
||||
};
|
||||
return QString::number(errCode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
BZip2IODevice::BZip2IODevice(QIODevice *dev, int blocksize)
|
||||
: d(new Private(this, dev, blocksize))
|
||||
{}
|
||||
|
||||
BZip2IODevice::~BZip2IODevice()
|
||||
{
|
||||
close();
|
||||
delete d;
|
||||
}
|
||||
|
||||
void BZip2IODevice::setBufferSize(unsigned int size)
|
||||
{
|
||||
d->iMaxBufSize = size > 1024 ? size : 1024;
|
||||
d->buffer.resize(d->iMaxBufSize);
|
||||
}
|
||||
|
||||
void BZip2IODevice::setBlockSize(unsigned int size)
|
||||
{
|
||||
if (isOpen())
|
||||
return;
|
||||
d->blocksize = size;
|
||||
}
|
||||
|
||||
bool BZip2IODevice::isSequential() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BZip2IODevice::open(OpenMode mode)
|
||||
{
|
||||
if ((mode & QIODevice::ReadWrite) == 0)
|
||||
{
|
||||
setErrorString(QStringLiteral("Unsupported OpenMode %1").arg(mode));
|
||||
return false;
|
||||
}
|
||||
if ((mode & QIODevice::ReadWrite) == QIODevice::ReadWrite)
|
||||
{
|
||||
setErrorString(QLatin1String("Can't read and write simultaniously"));
|
||||
return false;
|
||||
}
|
||||
if ((mode & QIODevice::ReadOnly) == QIODevice::ReadOnly)
|
||||
{
|
||||
if (!d->device)
|
||||
{
|
||||
setErrorString(QLatin1String("No QIODevice for reading available"));
|
||||
return false;
|
||||
}
|
||||
if ((d->device->openMode() & QIODevice::ReadOnly) == 0)
|
||||
{
|
||||
setErrorString(QLatin1String("QIODevice not opened for reading"));
|
||||
return false;
|
||||
}
|
||||
if (!d->initializeDecompress())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!d->device)
|
||||
{
|
||||
setErrorString(QLatin1String("No QIODevice for writing available"));
|
||||
return false;
|
||||
}
|
||||
if ((d->device->openMode() & QIODevice::WriteOnly) == 0)
|
||||
{
|
||||
setErrorString(QLatin1String("QIODevice not opened for writing"));
|
||||
return false;
|
||||
}
|
||||
if (!d->initializeCompress())
|
||||
return false;
|
||||
}
|
||||
setErrorString(QString());
|
||||
setOpenMode(mode);
|
||||
return QIODevice::open(mode);
|
||||
}
|
||||
|
||||
void BZip2IODevice::abort()
|
||||
{
|
||||
d->aborted = 1;
|
||||
}
|
||||
|
||||
void BZip2IODevice::close()
|
||||
{
|
||||
if (!isOpen())
|
||||
return;
|
||||
if ((openMode() & QIODevice::ReadOnly) == QIODevice::ReadOnly)
|
||||
d->finishDecompress();
|
||||
else
|
||||
d->finishCompress();
|
||||
QIODevice::close();
|
||||
}
|
||||
|
||||
bool BZip2IODevice::atEnd() const
|
||||
{
|
||||
if (!d->device)
|
||||
return true;
|
||||
return d->device->atEnd() && d->decomressStreamEnd && QIODevice::atEnd();
|
||||
}
|
||||
|
||||
qint64 BZip2IODevice::readData(char *data, qint64 maxSize)
|
||||
{
|
||||
return d->doDecompress(data, maxSize);
|
||||
}
|
||||
|
||||
qint64 BZip2IODevice::writeData(const char *data, qint64 maxSize)
|
||||
{
|
||||
return d->doCompress(data, maxSize);
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef BZIP2IODEVICE_H
|
||||
#define BZIP2IODEVICE_H
|
||||
|
||||
#include <QString>
|
||||
#include <QIODevice>
|
||||
|
||||
|
||||
|
||||
class BZip2IODevice : public QIODevice
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
|
||||
BZip2IODevice(QIODevice *dev, int blocksize = 5);
|
||||
virtual ~BZip2IODevice();
|
||||
|
||||
void abort();
|
||||
void setBufferSize(unsigned int size);
|
||||
void setBlockSize(unsigned int size);
|
||||
|
||||
|
||||
virtual bool isSequential() const;
|
||||
virtual bool open(OpenMode mode);
|
||||
virtual void close();
|
||||
virtual bool atEnd() const;
|
||||
protected:
|
||||
virtual qint64 readData(char *data, qint64 maxSize);
|
||||
virtual qint64 writeData(const char *data, qint64 maxSize);
|
||||
private:
|
||||
|
||||
class Private;
|
||||
friend class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,570 @@
|
||||
|
||||
|
||||
#include "nglcomp_compress.h"
|
||||
#include "nglcomp_bzip2iodevice.h"
|
||||
#include "nglcomp_tarfilter.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QtConcurrent>
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QFutureWatcher>
|
||||
#include <QIODevice>
|
||||
#include <QStringList>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
|
||||
Compress::Compress(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_u2_memFiles()
|
||||
, m_aborted(0)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("nglcompress"))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "c_tor";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QTimer *t = new QTimer(this);
|
||||
t->setInterval(1000);
|
||||
connect(t, &QTimer::timeout, this, [](){qWarning() << "still alive ...";});
|
||||
t->start();
|
||||
}
|
||||
|
||||
Compress::~Compress()
|
||||
{
|
||||
qWarning() << "## COMPRESS DTOR!!!!";
|
||||
|
||||
|
||||
DEBUG_LOG(m_logger) << "~";
|
||||
|
||||
abort();
|
||||
m_future.waitForFinished();
|
||||
DEBUG_LOG(m_logger) << "~ finished";
|
||||
}
|
||||
|
||||
void Compress::abort()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "ABORT";
|
||||
|
||||
m_aborted = 1;
|
||||
m_lastError = tr("Aborted");
|
||||
|
||||
if (m_currentTarFilter != nullptr)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "aborting tar";
|
||||
m_currentTarFilter->abort();
|
||||
}
|
||||
|
||||
if (m_currentBzipDevice != nullptr)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "aborting bzip";
|
||||
m_currentBzipDevice->abort();
|
||||
}
|
||||
}
|
||||
|
||||
void Compress::compress(QIODevice *out,
|
||||
const QString &filesRootDir,
|
||||
const QVector<FileInformations> &files,
|
||||
const QVector<FileInformations> &memFiles,
|
||||
const QString &destRootDir)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "compress #2";
|
||||
reset();
|
||||
setupFutureWatcher(QtConcurrent::run([this, out, filesRootDir, files, memFiles, destRootDir]() {
|
||||
return this->_compress(out, filesRootDir, files, memFiles, destRootDir);
|
||||
}));
|
||||
}
|
||||
|
||||
void Compress::compress(QIODevice *out, const QString &sourceDir, const QString &destRootDir)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "compress #1 source dir: " << sourceDir << ", destRootDir: " << destRootDir;
|
||||
reset();
|
||||
|
||||
QVector<Compress::FileInformations> inFiles = directoryFileList(sourceDir, true);
|
||||
|
||||
|
||||
int len = sourceDir.length();
|
||||
if (!sourceDir.endsWith(QLatin1Char('/')))
|
||||
len += 1;
|
||||
for (auto &fi : inFiles)
|
||||
{
|
||||
fi.inputFileName = fi.inputFileName.mid(len);
|
||||
fi.bAbsInputPath = false;
|
||||
DEBUG_LOG(m_logger) << "fi: ifm = " << fi.inputFileName << ", abs = " << fi.bAbsInputPath;
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "FI list size: " << inFiles.count();
|
||||
|
||||
compress(out, sourceDir, inFiles, {}, destRootDir);
|
||||
}
|
||||
|
||||
bool Compress::_compress(QIODevice *output,
|
||||
const QString &filesRootDir,
|
||||
const QVector<FileInformations> &files,
|
||||
const QVector<FileInformations> &memFiles,
|
||||
const QString &_destRootDir)
|
||||
{
|
||||
std::shared_ptr<int> invalidatePointersOnScopeLeave(nullptr, [this](int *)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "invalidatePointersOnScopeLeave #1";
|
||||
m_currentTarFilter = nullptr;
|
||||
m_currentBzipDevice = nullptr;
|
||||
});
|
||||
(void)invalidatePointersOnScopeLeave;
|
||||
|
||||
|
||||
if (!output || !output->isWritable())
|
||||
{
|
||||
m_lastError = tr("Device is not writeable");
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString destRootDir = _destRootDir.endsWith(QLatin1Char('/')) ?
|
||||
_destRootDir : _destRootDir + QLatin1Char('/');
|
||||
|
||||
const qint64 maxCount = files.count() + memFiles.count();
|
||||
qint64 count = 0;
|
||||
|
||||
DEBUG_LOG(m_logger) << "destRootDir = " << destRootDir << ", maxCount = " << maxCount << ", count = " << count;
|
||||
|
||||
BZip2IODevice bzip2(output, 9);
|
||||
m_currentBzipDevice = &bzip2;
|
||||
|
||||
if (!bzip2.open(QIODevice::WriteOnly))
|
||||
{
|
||||
m_lastError = bzip2.errorString();
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
qint64 bytesTotal = 0;
|
||||
for (const FileInformations &file : std::as_const(files))
|
||||
{
|
||||
if (m_aborted) return false;
|
||||
|
||||
const QString in = file.bAbsInputPath ? file.inputFileName : filesRootDir + QLatin1Char('/') + file.inputFileName;
|
||||
const QFileInfo fi(in);
|
||||
|
||||
bytesTotal += fi.size();
|
||||
}
|
||||
|
||||
|
||||
qint64 currentFileSize = 0;
|
||||
QString currentFileName;
|
||||
auto readCallback = [this, &maxCount, &count, ¤tFileSize, ¤tFileName, &bytesTotal](const qint64 iSize)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "readCallback: maxCount = " << maxCount << ", count = " << count
|
||||
<< ", currentFileSize = " << currentFileSize << ", currentFileName = " << currentFileName << ", iSize = " << iSize;
|
||||
emit compressInfoEx(maxCount, count, currentFileSize, (currentFileSize - iSize), currentFileName, bytesTotal);
|
||||
};
|
||||
|
||||
|
||||
TarFilter tf(&bzip2, readCallback);
|
||||
m_currentTarFilter = &tf;
|
||||
const QStringList paths = destRootDir.split(QLatin1Char('/'), Qt::SkipEmptyParts);
|
||||
QString curPath;
|
||||
|
||||
|
||||
DEBUG_LOG(m_logger) << "prepairing tar filter";
|
||||
for (const QString &path : paths)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "path = " << path;
|
||||
if (m_aborted) return false;
|
||||
|
||||
if (curPath.isEmpty())
|
||||
curPath = path + QLatin1Char('/');
|
||||
else
|
||||
curPath += path + QLatin1Char('/');
|
||||
|
||||
DEBUG_LOG(m_logger) << "curPath = " << curPath << ", filesRootDir.isEmpty() = " << filesRootDir.isEmpty();
|
||||
|
||||
if (filesRootDir.isEmpty())
|
||||
{
|
||||
if (!tf.addData(curPath, QByteArray(), true))
|
||||
{
|
||||
m_lastError = tf.lastError();
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!tf.addFile(filesRootDir, curPath))
|
||||
{
|
||||
m_lastError = tf.lastError();
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_c_sizeInfo.realSize = 0;
|
||||
for (const FileInformations &file : std::as_const(files))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "file = " << file.inputFileName;
|
||||
emit compressInfo(maxCount, ++count, file.inputFileName);
|
||||
|
||||
if (m_aborted) return false;
|
||||
|
||||
QString in = file.bAbsInputPath ? file.inputFileName : filesRootDir + QLatin1Char('/') + file.inputFileName;
|
||||
QString out = destRootDir + (file.outputFileName.isEmpty() ? file.inputFileName : file.outputFileName);
|
||||
|
||||
DEBUG_LOG(m_logger) << "in = " << in << ", out = " << out;
|
||||
|
||||
QFileInfo fi(in);
|
||||
|
||||
currentFileName = fi.fileName();
|
||||
currentFileSize = fi.size();
|
||||
m_c_sizeInfo.realSize += currentFileSize;
|
||||
|
||||
DEBUG_LOG(m_logger) << "currentFileName = " << currentFileName << ", currentFileSize = " << currentFileSize << "m_c_sizeInfo.realSize" << m_c_sizeInfo.realSize;
|
||||
|
||||
if (!tf.addFile(in, out))
|
||||
{
|
||||
m_lastError = tf.lastError();
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const FileInformations &file : std::as_const(memFiles))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "(mem) file = " << file.inputFileName;
|
||||
emit compressInfo(maxCount, ++count, file.inputFileName);
|
||||
|
||||
if (m_aborted) return false;
|
||||
|
||||
currentFileName = file.inputFileName;
|
||||
currentFileSize = file.fileData.size();
|
||||
m_c_sizeInfo.compressedSize += currentFileSize;
|
||||
|
||||
DEBUG_LOG(m_logger) << "(mem) currentFileName = " << currentFileName << ", currentFileSize = " << currentFileSize << "m_c_sizeInfo.realSize" << m_c_sizeInfo.realSize;
|
||||
|
||||
if (!tf.addData(file.inputFileName, file.fileData))
|
||||
{
|
||||
m_lastError = tf.lastError();
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DEBUG_LOG(m_logger) << "writing eos";
|
||||
tf.writeEOS();
|
||||
|
||||
DEBUG_LOG(m_logger) << "closing bzip";
|
||||
bzip2.close();
|
||||
|
||||
m_c_sizeInfo.compressedSize = output->size();
|
||||
DEBUG_LOG(m_logger) << "m_c_sizeInfo.compressedSize = " << m_c_sizeInfo.compressedSize;
|
||||
|
||||
m_success = true;
|
||||
|
||||
DEBUG_LOG(m_logger) << "DONE!";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Compress::uncompress(QIODevice *in, const QString &outDir)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "uncompress #1";
|
||||
reset();
|
||||
setupFutureWatcher(QtConcurrent::run([this, in, outDir](){ return this->_uncompressDirectory(in, outDir); }));
|
||||
}
|
||||
|
||||
bool Compress::_uncompressDirectory(QIODevice *input, const QString &outDir)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "_uncompressDirectory";
|
||||
|
||||
std::shared_ptr<int> invalidatePointersOnScopeLeave(nullptr, [this](int *)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "invalidatePointersOnScopeLeave #2";
|
||||
m_currentTarFilter = nullptr;
|
||||
m_currentBzipDevice = nullptr;
|
||||
});
|
||||
(void)invalidatePointersOnScopeLeave;
|
||||
|
||||
|
||||
if (!input || !input->isReadable())
|
||||
{
|
||||
m_lastError = tr("Device is not readable");
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BZip2IODevice bzip2(input, 9);
|
||||
m_currentBzipDevice = &bzip2;
|
||||
if (!bzip2.open(QIODevice::ReadOnly))
|
||||
{
|
||||
m_lastError = bzip2.errorString();
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TarFilter tf(&bzip2);
|
||||
m_currentTarFilter = &tf;
|
||||
TarFilter::FileInformations tfi;
|
||||
QDir dir(outDir);
|
||||
|
||||
DEBUG_LOG(m_logger) << "dir = " << dir.path();
|
||||
|
||||
while (tf.getData(tfi))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "tfi.fileName = " << tfi.fileName;
|
||||
emit uncompressInfo(input->size(), input->pos(), tfi.fileName);
|
||||
if (m_aborted) return false;
|
||||
|
||||
QString path = outDir + QLatin1Char('/') + tfi.fileName;
|
||||
DEBUG_LOG(m_logger) << "path = " << path;
|
||||
if (tfi.fileType == TarFilter::directory)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "tfi.fileType == TarFilter::directory";
|
||||
if (!dir.exists(path) && !dir.mkpath(path))
|
||||
{
|
||||
m_lastError = tr("Error creating directory %1").arg(outDir);
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
QString pathName = QFileInfo(path).absolutePath();
|
||||
if (!dir.exists(pathName) && !dir.mkpath(pathName))
|
||||
{
|
||||
m_lastError = tr("Error creating directory %1").arg(pathName);
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly))
|
||||
{
|
||||
m_lastError = tr("Error opening %1 for writing (%2)").arg(path, f.errorString());
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!tf.getData(&f))
|
||||
{
|
||||
m_lastError = tf.lastError();
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "uncompress done!";
|
||||
m_success = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Compress::uncompress(QIODevice *in)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "uncompress #2!";
|
||||
reset();
|
||||
setupFutureWatcher(QtConcurrent::run([this, in](){ return this->_uncompressBuffer(in); }));
|
||||
}
|
||||
|
||||
bool Compress::_uncompressBuffer(QIODevice *input)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "uncompressBuffer";
|
||||
|
||||
std::shared_ptr<int> invalidatePointersOnScopeLeave(nullptr, [this](int *)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "invalidatePointersOnScopeLeave #2";
|
||||
m_currentTarFilter = nullptr;
|
||||
m_currentBzipDevice = nullptr;
|
||||
});
|
||||
(void)invalidatePointersOnScopeLeave;
|
||||
|
||||
|
||||
if (!input->isReadable())
|
||||
{
|
||||
m_lastError = tr("Device is not readable");
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BZip2IODevice bzip2(input, 9);
|
||||
m_currentBzipDevice = &bzip2;
|
||||
if (!bzip2.open(QIODevice::ReadOnly))
|
||||
{
|
||||
m_lastError = bzip2.errorString();
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TarFilter tf(&bzip2);
|
||||
m_currentTarFilter = &tf;
|
||||
TarFilter::FileInformations tfi;
|
||||
while (tf.getData(tfi))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "tfi.fileName = " << tfi.fileName;
|
||||
emit uncompressInfo(input->size(), input->pos(), tfi.fileName);
|
||||
if (m_aborted) return false;
|
||||
|
||||
FileInformations fi;
|
||||
fi.outputFileName = tfi.fileName;
|
||||
|
||||
DEBUG_LOG(m_logger) << "fi.outputFileName = " << fi.outputFileName;
|
||||
|
||||
if (tfi.fileType == TarFilter::directory)
|
||||
{
|
||||
fi.outputFileName += QLatin1Char('/');
|
||||
DEBUG_LOG(m_logger) << "> fi.outputFileName = " << fi.outputFileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "stuff";
|
||||
|
||||
QBuffer buf(&fi.fileData);
|
||||
if (!buf.open(QIODevice::WriteOnly))
|
||||
{
|
||||
m_lastError = tr("Error opening buffer for writing (%1)").arg(buf.errorString());
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
if (!tf.getData(&buf))
|
||||
{
|
||||
m_lastError = tf.lastError();
|
||||
ERROR_LOG(m_logger) << "compress failed: " << m_lastError;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fi.bAbsInputPath = fi.outputFileName.startsWith(QLatin1Char('/'));
|
||||
DEBUG_LOG(m_logger) << "added fi.bAbsInputPath = " << fi.bAbsInputPath;
|
||||
m_u2_memFiles += fi;
|
||||
}
|
||||
|
||||
m_success = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Compress::success() const
|
||||
{
|
||||
|
||||
ERROR_LOG(m_logger) << "success? " << (m_future.isFinished() && !m_future.isCanceled() && m_success);
|
||||
ERROR_LOG(m_logger) << "success details: "
|
||||
<< "success m_future.isFinished() = " << (m_future.isFinished())
|
||||
<< " !m_future.isCanceled() = " << !m_future.isCanceled()
|
||||
<< " m_success = " << m_success;
|
||||
return (m_future.isFinished() && !m_future.isCanceled() && m_success);
|
||||
}
|
||||
|
||||
bool Compress::waitForFinished()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "waitForFinished...";
|
||||
m_future.waitForFinished();
|
||||
DEBUG_LOG(m_logger) << "waitForFinished... done!";
|
||||
return success();
|
||||
}
|
||||
|
||||
QVector<Compress::FileInformations> Compress::directoryFileList(const QString &dir, bool recursive)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "directoryFileList";
|
||||
|
||||
QVector<FileInformations> sl_absolute;
|
||||
QDir d(dir);
|
||||
|
||||
if (d.exists())
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "exists";
|
||||
const auto entries = d.entryList(QDir::Files);
|
||||
sl_absolute.reserve(sl_absolute.size() + entries.size());
|
||||
for (const QString &entry : entries)
|
||||
sl_absolute << FileInformations(QFileInfo(d, entry).absoluteFilePath(), true);
|
||||
}
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "recursive";
|
||||
const auto entries = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
sl_absolute.reserve(sl_absolute.size() + entries.size());
|
||||
for (const QString &s : entries)
|
||||
sl_absolute << directoryFileList(dir + QLatin1Char('/') + s, true);
|
||||
}
|
||||
|
||||
DEBUG_LOG(m_logger) << "done";
|
||||
return sl_absolute;
|
||||
}
|
||||
|
||||
void Compress::reset()
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "reset";
|
||||
m_u2_memFiles.clear();
|
||||
|
||||
m_lastError.clear();
|
||||
|
||||
m_c_sizeInfo.realSize = 0;
|
||||
m_c_sizeInfo.compressedSize = 0;
|
||||
|
||||
m_success = false;
|
||||
}
|
||||
|
||||
void Compress::setupFutureWatcher(const QFuture<bool> &future)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "setupFutureWatcher";
|
||||
m_future = future;
|
||||
QFutureWatcher<bool> *fw = new QFutureWatcher<bool>(this);
|
||||
QObject::connect(fw, &QObject::destroyed, this, [this]() { DEBUG_LOG(m_logger) << "Future Watcher destroyed"; });
|
||||
QObject::connect(fw, &QFutureWatcher<bool>::finished, this, &Compress::finished);
|
||||
QObject::connect(fw, &QFutureWatcher<bool>::finished, fw, &QObject::deleteLater);
|
||||
QObject::connect(fw, &QFutureWatcher<bool>::finished, fw, [this]() { DEBUG_LOG(m_logger) << "Future Watcher about to be deleted"; });
|
||||
fw->setFuture(m_future);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "nglcomp_export.h"
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QFuture>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
class QIODevice;
|
||||
class TarFilter;
|
||||
class BZip2IODevice;
|
||||
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class NGLCOMPRESSION_EXPORT Compress : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct FileInformations
|
||||
{
|
||||
QString inputFileName;
|
||||
QString outputFileName;
|
||||
QByteArray fileData;
|
||||
bool bAbsInputPath = true;
|
||||
FileInformations() = default;
|
||||
|
||||
FileInformations(const QString &name, bool isAbsolute)
|
||||
: inputFileName(name)
|
||||
, bAbsInputPath(isAbsolute)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
struct FileSizeInfo
|
||||
{
|
||||
qint64 realSize = 0;
|
||||
qint64 compressedSize = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
explicit Compress(QObject *parent = nullptr);
|
||||
|
||||
~Compress();
|
||||
|
||||
|
||||
|
||||
QVector<FileInformations> getMemFiles() const { return m_u2_memFiles; }
|
||||
|
||||
|
||||
FileSizeInfo getFileSizeInfo() const { return m_c_sizeInfo; }
|
||||
|
||||
|
||||
bool success() const;
|
||||
|
||||
|
||||
|
||||
void compress(QIODevice *out,
|
||||
const QString &sourceRootDir,
|
||||
const QVector<FileInformations> &files,
|
||||
const QVector<FileInformations> &memFiles,
|
||||
const QString &destRootDir);
|
||||
|
||||
|
||||
|
||||
void compress(QIODevice *out,
|
||||
const QString &sourceDir,
|
||||
const QString &destRootDir);
|
||||
|
||||
|
||||
void uncompress(QIODevice *in,
|
||||
const QString &outDir);
|
||||
|
||||
|
||||
void uncompress(QIODevice *in);
|
||||
|
||||
|
||||
QString lastError() const { return m_lastError; }
|
||||
|
||||
|
||||
|
||||
bool waitForFinished();
|
||||
|
||||
public Q_SLOTS:
|
||||
void abort();
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
|
||||
void uncompressInfo(qint64 totalSize, qint64 curPos, const QString &curName);
|
||||
|
||||
|
||||
void compressInfo(qint64 totalFiles, qint64 curFile, const QString &curName);
|
||||
void compressInfoEx(qint64 totalFiles, qint64 curFile, qint64 totalSize, qint64 curPos, const QString &curName, const qint64 bytesTotal);
|
||||
|
||||
|
||||
|
||||
void error(const QString &errorString);
|
||||
|
||||
|
||||
|
||||
void finished();
|
||||
private:
|
||||
|
||||
bool _compress(QIODevice *output,
|
||||
const QString &filesRootDir,
|
||||
const QVector<FileInformations> &files,
|
||||
const QVector<FileInformations> &memFiles,
|
||||
const QString &_destRootDir);
|
||||
bool _uncompressDirectory(QIODevice *input, const QString &outDir);
|
||||
bool _uncompressBuffer(QIODevice *input);
|
||||
|
||||
void reset();
|
||||
void setupFutureWatcher(const QFuture<bool> &future);
|
||||
|
||||
QVector<FileInformations> directoryFileList(const QString &dir, bool recursive = false);
|
||||
|
||||
FileSizeInfo m_c_sizeInfo;
|
||||
QVector<FileInformations> m_u2_memFiles;
|
||||
|
||||
QString m_lastError;
|
||||
QAtomicInt m_aborted;
|
||||
|
||||
TarFilter *m_currentTarFilter { nullptr };
|
||||
BZip2IODevice *m_currentBzipDevice { nullptr };
|
||||
|
||||
QFuture<bool> m_future;
|
||||
bool m_success { false };
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
}
|
||||
Q_DECLARE_TYPEINFO(NGL::Compress::FileInformations, Q_MOVABLE_TYPE);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifndef NGLCOMPRESSION_EXPORT
|
||||
# ifdef BUILD_STATIC_LIBRARIES
|
||||
# define NGLCOMPRESSION_EXPORT
|
||||
# else
|
||||
# if defined(MAKE_NGLCOMPRESSION_LIB)
|
||||
# define NGLCOMPRESSION_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define NGLCOMPRESSION_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,522 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QFile>
|
||||
#include <QIODevice>
|
||||
#include <QFileInfo>
|
||||
#include <QDateTime>
|
||||
// Use UTF-8 conversions instead of QTextCodec (Qt6)
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "nglcomp_tarfilter.h"
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TarFilter::Private
|
||||
{
|
||||
public:
|
||||
Private(QIODevice *dev)
|
||||
: device(dev)
|
||||
, iBufSize(1024 * 1024)
|
||||
, nextChunkSize(-1)
|
||||
{}
|
||||
|
||||
struct tar_posix_header
|
||||
{
|
||||
char name[100];
|
||||
char mode[8];
|
||||
char uid[8];
|
||||
char gid[8];
|
||||
char size[12];
|
||||
char mtime[12];
|
||||
char chksum[8];
|
||||
char typeflag;
|
||||
char linkname[100];
|
||||
char magic[6];
|
||||
char version[2];
|
||||
char uname[32];
|
||||
char gname[32];
|
||||
char devmajor[8];
|
||||
char devminor[8];
|
||||
char prefix[155];
|
||||
char unused[12];
|
||||
};
|
||||
bool fi2tph(const FileInformations &infos);
|
||||
bool addHeader(const FileInformations &infos);
|
||||
|
||||
bool tph2fi(FileInformations &infos);
|
||||
bool getHeader(FileInformations &infos);
|
||||
bool checkDevice(QIODevice *in, QIODevice *out);
|
||||
bool copyData(QIODevice *in, QIODevice *out, qint64 iSize);
|
||||
|
||||
QIODevice *device;
|
||||
QString lastError;
|
||||
unsigned int iBufSize;
|
||||
qint64 nextChunkSize;
|
||||
QAtomicInt aborted { 0 };
|
||||
std::function<void(qint64)> readCallback;
|
||||
};
|
||||
|
||||
bool TarFilter::Private::fi2tph(const FileInformations &infos)
|
||||
{
|
||||
QByteArray fn = infos.fileName.toUtf8();
|
||||
#ifdef Q_OS_WIN
|
||||
if (fn.size() > 3 && fn[1] == ':' && (fn[2] == '\\' || fn[2] == '/'))
|
||||
fn = fn.mid(3);
|
||||
#endif
|
||||
if (fn.startsWith('/'))
|
||||
fn = fn.mid(1);
|
||||
|
||||
long unsigned int fileSize = infos.fileType == directory ? 0 : infos.fileSize;
|
||||
long unsigned int mtime = infos.mtime != -1 ? infos.mtime : time(nullptr);
|
||||
tar_posix_header hdr;
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
QByteArray fn_part1 = fn.left(sizeof(hdr.name));
|
||||
memcpy(hdr.name, fn_part1.constData(), fn_part1.size());
|
||||
sprintf(hdr.mode, "%07x", 0x0fff & infos.mode);
|
||||
sprintf(hdr.uid, "%07o", infos.uid);
|
||||
sprintf(hdr.gid, "%07o", infos.gid);
|
||||
sprintf(hdr.size, "%011lo", fileSize);
|
||||
sprintf(hdr.mtime, "%011lo", mtime);
|
||||
memset(hdr.chksum, ' ', sizeof(hdr.chksum));
|
||||
hdr.typeflag = infos.fileType;
|
||||
|
||||
memcpy(hdr.magic, "ustar ",sizeof(hdr.magic) + sizeof(hdr.version));
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
|
||||
#else
|
||||
{
|
||||
QByteArray unameBa = infos.uname.toUtf8();
|
||||
QByteArray gnameBa = infos.gname.toUtf8();
|
||||
qstrncpy(hdr.uname, unameBa.constData(), sizeof(hdr.uname));
|
||||
qstrncpy(hdr.gname, gnameBa.constData(), sizeof(hdr.gname));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned int checksum = 0;
|
||||
const unsigned char *ptr = reinterpret_cast<const unsigned char *>(&hdr);
|
||||
for (unsigned i = 0; i < sizeof(hdr); i++)
|
||||
checksum += ptr[i];
|
||||
constexpr int octalMax = 01000000;
|
||||
while (checksum > octalMax)
|
||||
checksum -= octalMax;
|
||||
sprintf(hdr.chksum, "%06o", checksum);
|
||||
|
||||
if (!device->write((const char *)&hdr, sizeof(hdr)))
|
||||
{
|
||||
lastError = QLatin1String("Error writing to QIODevice");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TarFilter::Private::addHeader(const FileInformations &infos)
|
||||
{
|
||||
|
||||
|
||||
QByteArray fn = infos.fileName.toUtf8();
|
||||
#ifdef Q_OS_WIN
|
||||
if (fn.size() > 3 && fn[1] == ':' && (fn[2] == '\\' || fn[2] == '/'))
|
||||
fn = fn.mid(3);
|
||||
#endif
|
||||
if (fn.startsWith('/'))
|
||||
fn = fn.mid(1);
|
||||
if (fn.size() > 512)
|
||||
{
|
||||
lastError = QLatin1String("Can't handle filenames with more then 512 bytes");
|
||||
return false;
|
||||
}
|
||||
if (fn.size() > 100)
|
||||
{
|
||||
FileInformations longlink;
|
||||
longlink.fileName = QLatin1String("././@LongLink");
|
||||
longlink.fileType = gnu_longname;
|
||||
longlink.fileSize = strlen(fn.constData()) + 1;
|
||||
longlink.mtime = 0;
|
||||
longlink.uid = 0;
|
||||
longlink.gid = 0;
|
||||
#ifndef Q_OS_WIN
|
||||
struct passwd *p = getpwuid(longlink.uid);
|
||||
if (p)
|
||||
longlink.uname = QString::fromUtf8(p->pw_name);
|
||||
struct group *g = getgrgid(longlink.gid);
|
||||
if (g)
|
||||
longlink.gname = QString::fromUtf8(g->gr_name);
|
||||
#endif
|
||||
fi2tph(longlink);
|
||||
QByteArray ba(512, '\0');
|
||||
qstrncpy(ba.data(), fn.constData(), ba.size());
|
||||
if (!device->write(ba.constData(), ba.size()))
|
||||
{
|
||||
lastError = QLatin1String("Error writing to QIODevice");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!fi2tph(infos))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TarFilter::Private::tph2fi(FileInformations &infos)
|
||||
{
|
||||
Private::tar_posix_header hdr;
|
||||
do
|
||||
{
|
||||
int iBytesRead = device->read((char *)&hdr, sizeof(hdr));
|
||||
if (iBytesRead != sizeof(hdr))
|
||||
{
|
||||
if (iBytesRead == 0 && device->atEnd())
|
||||
{
|
||||
lastError = QString();
|
||||
return false;
|
||||
}
|
||||
lastError = QStringLiteral("Error reading from QIODevice! needed: %1, got: %2").arg(sizeof(hdr)).arg(iBytesRead);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (memcmp(hdr.magic, "\0\0\0\0\0\0", sizeof(hdr.magic)) == 0);
|
||||
if (memcmp(hdr.magic, "ustar ", sizeof(hdr.magic)))
|
||||
{
|
||||
lastError = QLatin1String("The archive is not supported");
|
||||
return false;
|
||||
}
|
||||
int checksum = QByteArray(hdr.chksum, sizeof(hdr.chksum)).toUInt(nullptr, 8);
|
||||
memset(hdr.chksum, ' ', sizeof(hdr.chksum));
|
||||
int realChecksum = 0;
|
||||
const unsigned char *ptr = reinterpret_cast<const unsigned char *>(&hdr);
|
||||
for (unsigned i = 0; i < sizeof(hdr); i++)
|
||||
realChecksum += ptr[i];
|
||||
if (checksum != realChecksum)
|
||||
{
|
||||
lastError = QStringLiteral("Checksum does not match! needed: %1, got: %2").arg(checksum).arg(realChecksum);
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray ba(hdr.name, sizeof(hdr.name));
|
||||
ba.resize(qstrlen(ba.constData()));
|
||||
infos.fileName = QString::fromUtf8(ba.constData());
|
||||
infos.fileSize = QByteArray(hdr.size, sizeof(hdr.size)).toUInt(nullptr, 8);
|
||||
infos.mode = static_cast<QFile::Permissions>(QByteArray(hdr.mode, sizeof(hdr.mode)).toUInt(nullptr, 16));
|
||||
infos.uid = QByteArray(hdr.uid, sizeof(hdr.uid)).toUInt(nullptr, 8);
|
||||
infos.gid = QByteArray(hdr.gid, sizeof(hdr.gid)).toUInt(nullptr, 8);
|
||||
infos.mtime = QByteArray(hdr.mtime, sizeof(hdr.mtime)).toUInt(nullptr, 8);
|
||||
infos.fileType = (FileType)hdr.typeflag;
|
||||
infos.uname = QString::fromUtf8(QByteArray(hdr.uname, sizeof(hdr.uname)).constData());
|
||||
infos.gname = QString::fromUtf8(QByteArray(hdr.gname, sizeof(hdr.gname)).constData());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TarFilter::Private::getHeader(FileInformations &infos)
|
||||
{
|
||||
if (!tph2fi(infos))
|
||||
return false;
|
||||
switch (infos.fileType)
|
||||
{
|
||||
case regular:
|
||||
case regular2:
|
||||
case directory:
|
||||
case gnu_longname:
|
||||
break;
|
||||
default:
|
||||
lastError = QStringLiteral("Unsupported fileType %1").arg(infos.fileType);
|
||||
return false;
|
||||
}
|
||||
if (infos.fileType == gnu_longname)
|
||||
{
|
||||
const QByteArray fn = device->read(512);
|
||||
if (fn.size() != 512)
|
||||
{
|
||||
lastError = QLatin1String("Unexpected end of inputstream");
|
||||
return false;
|
||||
}
|
||||
if (!tph2fi(infos))
|
||||
return false;
|
||||
const int len = qstrlen(fn.constData());
|
||||
infos.fileName = QString::fromUtf8(fn.constData(), len);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TarFilter::Private::checkDevice(QIODevice *in, QIODevice *out)
|
||||
{
|
||||
if (!in)
|
||||
{
|
||||
lastError = QLatin1String("No QIODevice for reading available");
|
||||
return false;
|
||||
}
|
||||
if ((in->openMode() & QIODevice::ReadOnly) == 0)
|
||||
{
|
||||
lastError = QLatin1String("QIODevice not opened for reading");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!out)
|
||||
{
|
||||
lastError = QLatin1String("No QIODevice for writing available");
|
||||
return false;
|
||||
}
|
||||
if ((out->openMode() & QIODevice::WriteOnly) == 0)
|
||||
{
|
||||
lastError = QLatin1String("QIODevice not opened for writing");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TarFilter::Private::copyData(QIODevice *in, QIODevice *out, qint64 iSize)
|
||||
{
|
||||
if (iSize <= 0)
|
||||
return true;
|
||||
|
||||
while (iSize > 0)
|
||||
{
|
||||
if (aborted) return false;
|
||||
|
||||
const qint64 iBytesToRead = iSize > iBufSize ? iBufSize : iSize;
|
||||
const QByteArray ba = in->read(iBytesToRead);
|
||||
|
||||
if (aborted) return false;
|
||||
|
||||
if (ba.size() == 0)
|
||||
{
|
||||
lastError = QStringLiteral("Could not read %1 bytes from QIODevice").arg(iBytesToRead);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out->write(ba) != ba.size())
|
||||
{
|
||||
lastError = QStringLiteral("Could not write %1 bytes to QIODevice").arg(ba.size());
|
||||
return false;
|
||||
}
|
||||
iSize -= ba.size();
|
||||
|
||||
if (readCallback)
|
||||
readCallback(iSize);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TarFilter::TarFilter(QIODevice *dev, const std::function<void(qint64)> &readCallback)
|
||||
: d(new Private(dev))
|
||||
{
|
||||
d->readCallback = readCallback;
|
||||
}
|
||||
|
||||
TarFilter::~TarFilter()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void TarFilter::abort()
|
||||
{
|
||||
d->aborted = 1;
|
||||
}
|
||||
|
||||
void TarFilter::setBufferSize(unsigned int size)
|
||||
{
|
||||
d->iBufSize = size > 1024 ? size : 1024;
|
||||
}
|
||||
|
||||
bool TarFilter::error() const
|
||||
{
|
||||
return !d->lastError.isEmpty();
|
||||
}
|
||||
|
||||
QString TarFilter::lastError() const
|
||||
{
|
||||
return d->lastError;
|
||||
}
|
||||
|
||||
bool TarFilter::addFile(const QString &filename, const QString &filenameInArchive)
|
||||
{
|
||||
QFileInfo fi(filename);
|
||||
if (!fi.exists())
|
||||
{
|
||||
d->lastError = QStringLiteral("File %1 does not exist").arg(filename);
|
||||
return false;
|
||||
}
|
||||
if (!(fi.isDir() || fi.isFile()))
|
||||
{
|
||||
d->lastError = QStringLiteral("%1 isn't a directory or a regular file").arg(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
FileInformations fInfos;
|
||||
fInfos.fileName = (filenameInArchive.isEmpty() ? filename : filenameInArchive);
|
||||
fInfos.fileSize = fi.isDir() ? 0 : fi.size();
|
||||
fInfos.mode = fi.permissions() & 0x0fff;
|
||||
#ifdef Q_OS_WIN
|
||||
fInfos.uid = 1000;
|
||||
fInfos.gid = 100;
|
||||
#else
|
||||
fInfos.uid = fi.ownerId();
|
||||
fInfos.gid = fi.groupId();
|
||||
fInfos.uname = fi.owner();
|
||||
fInfos.gname = fi.group();
|
||||
#endif
|
||||
fInfos.mtime = fi.lastModified().toSecsSinceEpoch();
|
||||
fInfos.fileType = fi.isDir() ? directory : regular;
|
||||
|
||||
if (fi.isDir())
|
||||
{
|
||||
QBuffer buf;
|
||||
buf.open(QIODevice::ReadOnly);
|
||||
return addData(fInfos, &buf);
|
||||
}
|
||||
QFile f(filename);
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
{
|
||||
d->lastError = QStringLiteral("Error opening %1 for reading").arg(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->aborted) return false;
|
||||
|
||||
return addData(fInfos, &f);
|
||||
}
|
||||
|
||||
bool TarFilter::addData(const QString &filename, const QByteArray &data, bool bIsDir)
|
||||
{
|
||||
if (d->aborted) return false;
|
||||
|
||||
FileInformations fi;
|
||||
fi.fileName = filename;
|
||||
fi.fileSize = bIsDir ? 0 : data.size();
|
||||
fi.mode = static_cast<QFile::Permissions>(bIsDir ? 0x0755 : 0x0644);
|
||||
fi.fileType = bIsDir ? directory : regular;
|
||||
#ifndef Q_OS_WIN
|
||||
fi.uid = getuid();
|
||||
fi.gid = getgid();
|
||||
char name[256];
|
||||
struct passwd *p_ptr = nullptr;
|
||||
struct passwd p;
|
||||
getpwuid_r(fi.uid, &p, name, sizeof(name) - 1, &p_ptr);
|
||||
if (p_ptr)
|
||||
fi.uname = QString::fromUtf8(p_ptr->pw_name);
|
||||
struct group *g_ptr;
|
||||
struct group g;
|
||||
getgrgid_r(fi.gid, &g, name, sizeof(name) - 1, &g_ptr);
|
||||
if (g_ptr)
|
||||
fi.gname = QString::fromUtf8(g_ptr->gr_name);
|
||||
#endif
|
||||
|
||||
QBuffer buf(const_cast<QByteArray *>(&data));
|
||||
if (!buf.open(QIODevice::ReadOnly))
|
||||
{
|
||||
d->lastError = QLatin1String("Internal Error - can't open QBuffer for reading!");
|
||||
return false;
|
||||
}
|
||||
return addData(fi, &buf);
|
||||
}
|
||||
|
||||
bool TarFilter::addData(const FileInformations &fi, QIODevice *in)
|
||||
{
|
||||
if (d->aborted) return false;
|
||||
|
||||
if (!d->checkDevice(in, d->device))
|
||||
return false;
|
||||
|
||||
if (!d->addHeader(fi))
|
||||
return false;
|
||||
|
||||
if (fi.fileSize)
|
||||
{
|
||||
if (!d->copyData(in, d->device, fi.fileSize))
|
||||
return false;
|
||||
|
||||
int align = fi.fileSize % 512;
|
||||
if (align)
|
||||
{
|
||||
align = 512 - align;
|
||||
QByteArray ba2(align, '\0');
|
||||
d->device->write(ba2.constData(), align);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TarFilter::writeEOS()
|
||||
{
|
||||
|
||||
const int size = sizeof(TarFilter::Private::tar_posix_header) * 2;
|
||||
QByteArray ba(size, '\0');
|
||||
return (d->device->write(ba) == size);
|
||||
}
|
||||
|
||||
bool TarFilter::getData(FileInformations &fi)
|
||||
{
|
||||
if (d->aborted) return false;
|
||||
|
||||
d->nextChunkSize = -1;
|
||||
if (!d->getHeader(fi))
|
||||
return false;
|
||||
d->nextChunkSize = fi.fileSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TarFilter::getData(QIODevice *out)
|
||||
{
|
||||
if (d->aborted) return false;
|
||||
|
||||
if (!d->checkDevice(d->device, out))
|
||||
return false;
|
||||
|
||||
if (d->nextChunkSize < 0)
|
||||
{
|
||||
d->lastError = QLatin1String("No informations about next chunk size. Did you call getData(FileInformations &fi) before?");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d->copyData(d->device, out, d->nextChunkSize))
|
||||
return false;
|
||||
|
||||
int align = d->nextChunkSize % 512;
|
||||
if (align)
|
||||
d->device->read(512 - align);
|
||||
|
||||
d->nextChunkSize = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TarFilter::getData(FileInformations &fi, QByteArray &data)
|
||||
{
|
||||
if (!getData(fi))
|
||||
return false;
|
||||
|
||||
QBuffer buf(&data);
|
||||
buf.open(QIODevice::WriteOnly);
|
||||
return getData(&buf);
|
||||
}
|
||||
|
||||
bool TarFilter::getData(FileInformations &fi, QIODevice *out)
|
||||
{
|
||||
if (!getData(fi))
|
||||
return false;
|
||||
|
||||
return getData(out);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef TARFILTER_H
|
||||
#define TARFILTER_H
|
||||
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <functional>
|
||||
|
||||
class QIODevice;
|
||||
class QByteArray;
|
||||
|
||||
|
||||
|
||||
class TarFilter
|
||||
{
|
||||
public:
|
||||
Q_DISABLE_COPY(TarFilter)
|
||||
|
||||
enum FileType
|
||||
{
|
||||
regular = '0',
|
||||
regular2 = '\0',
|
||||
link = '1',
|
||||
reserved = '2',
|
||||
charType = '3',
|
||||
block = '4',
|
||||
directory = '5',
|
||||
fifo = '6',
|
||||
reserved2 = '7',
|
||||
gnu_longLink = 'K',
|
||||
gnu_longname = 'L',
|
||||
};
|
||||
struct FileInformations
|
||||
{
|
||||
QString fileName;
|
||||
qint64 fileSize = 0;
|
||||
int uid = 1000;
|
||||
int gid = 100;
|
||||
QString uname;
|
||||
QString gname;
|
||||
time_t mtime = -1;
|
||||
FileType fileType = regular;
|
||||
QFile::Permissions mode = QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther;
|
||||
};
|
||||
|
||||
|
||||
|
||||
TarFilter(QIODevice *dev, const std::function<void(qint64)> &readCallback = nullptr);
|
||||
~TarFilter();
|
||||
|
||||
void abort();
|
||||
void setBufferSize(unsigned int size);
|
||||
bool error() const;
|
||||
QString lastError() const;
|
||||
|
||||
|
||||
bool addData(const FileInformations &fi, QIODevice *in);
|
||||
|
||||
bool addFile(const QString &filename, const QString &filenameInArchive = QString());
|
||||
bool addData(const QString &filename, const QByteArray &data = QByteArray(), bool bIsDir = false);
|
||||
|
||||
bool writeEOS();
|
||||
|
||||
|
||||
bool getData(FileInformations &fi, QIODevice *out);
|
||||
|
||||
bool getData(FileInformations &fi);
|
||||
bool getData(QIODevice *out);
|
||||
bool getData(FileInformations &fi, QByteArray &data);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,175 @@
|
||||
|
||||
|
||||
#include "nglcomp_test.h"
|
||||
|
||||
#include "nglcomp_compress.h"
|
||||
|
||||
#include <ngltools/ngltools_filesystem.h>
|
||||
|
||||
#include <nglapplication/nglcoreapplication.h>
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
|
||||
|
||||
void nglcomp_test::initTestCase()
|
||||
{
|
||||
m_tempDirIn.setAutoRemove(true);
|
||||
m_tempDirOut.setAutoRemove(true);
|
||||
QVERIFY(m_tempDirIn.isValid());
|
||||
QVERIFY(m_tempDirOut.isValid());
|
||||
QVERIFY(m_tf.open());
|
||||
m_tf.setAutoRemove(true);
|
||||
|
||||
m_fileContent[QLatin1String("1.txt")] = "111111111111111111111111";
|
||||
m_fileContent[QLatin1String("sub1/s1.txt")] = "222222222222222222222222";
|
||||
m_fileContent[QLatin1String("sub1/sub11/s11.txt")] = "333333333333333333333333";
|
||||
m_fileContent[QLatin1String("sub1/sub12/s12.txt")] = "444444444444444444444444";
|
||||
m_fileContent[QLatin1String("sub2/2.txt")] = "555555555555555555555555";
|
||||
}
|
||||
|
||||
|
||||
void nglcomp_test::testCompressFile()
|
||||
{
|
||||
QDir dir;
|
||||
const auto directories = {
|
||||
QLatin1String("sub1"),
|
||||
QLatin1String("sub1/sub11"),
|
||||
QLatin1String("sub1/sub12"),
|
||||
QLatin1String("sub2")
|
||||
};
|
||||
for (const QLatin1String &directory : directories)
|
||||
QVERIFY(dir.mkdir(subPathIn(directory)));
|
||||
|
||||
for (auto it = m_fileContent.cbegin(); it != m_fileContent.cend(); ++it)
|
||||
writeTestFile(it.key(), it.value());
|
||||
|
||||
NGL::Compress c;
|
||||
c.compress(&m_tf, subPathIn(QString()), QLatin1String("out"));
|
||||
QVERIFY(c.waitForFinished());
|
||||
}
|
||||
|
||||
|
||||
void nglcomp_test::testCompressMemory()
|
||||
{
|
||||
QBuffer f(&m_ba);
|
||||
QVERIFY(f.open(QIODevice::WriteOnly));
|
||||
QVector<NGL::Compress::FileInformations> fi1;
|
||||
QVector<NGL::Compress::FileInformations> fi2;
|
||||
NGL::Compress::FileInformations fi;
|
||||
|
||||
fi.inputFileName = QLatin1String("chris.txt");
|
||||
fi.fileData = QByteArray("dumdidum\n");
|
||||
fi2 += fi;
|
||||
|
||||
fi.inputFileName = QLatin1String("blub/chris.txt");
|
||||
fi.fileData = QByteArray("dumdidum im Verzeichnis blub\n");
|
||||
fi2 += fi;
|
||||
|
||||
fi.inputFileName = QLatin1String("blub/blub/chris2.txt");
|
||||
fi.fileData = QByteArray("dumdidum im Verzeichnis blub/blub\n");
|
||||
fi2 += fi;
|
||||
|
||||
NGL::Compress c;
|
||||
c.compress(&f, QString(), fi1, fi2, QLatin1String("blub"));
|
||||
QVERIFY(c.waitForFinished());
|
||||
f.close();
|
||||
}
|
||||
|
||||
void nglcomp_test::testUncompressMemory()
|
||||
{
|
||||
QBuffer f(&m_ba);
|
||||
QVERIFY(f.open(QIODevice::ReadOnly));
|
||||
NGL::Compress c;
|
||||
c.uncompress(&f, subPathIn(QString()));
|
||||
QVERIFY(c.waitForFinished());
|
||||
f.close();
|
||||
|
||||
QVERIFY(f.open(QIODevice::ReadOnly));
|
||||
c.uncompress(&f);
|
||||
QVERIFY(c.waitForFinished());
|
||||
QVector<NGL::Compress::FileInformations> fi1 = c.getMemFiles();
|
||||
QVERIFY(fi1.count() == 4);
|
||||
f.close();
|
||||
}
|
||||
|
||||
void nglcomp_test::testUncompressFile()
|
||||
{
|
||||
NGL::Compress c;
|
||||
m_tf.seek(0);
|
||||
c.uncompress(&m_tf, subPathOut(QString()));
|
||||
QVERIFY(c.waitForFinished());
|
||||
|
||||
for (auto it = m_fileContent.cbegin(); it != m_fileContent.cend(); ++it)
|
||||
checkContent(it.key(), it.value());
|
||||
}
|
||||
|
||||
void nglcomp_test::cleanupTestCase()
|
||||
{
|
||||
m_tf.close();
|
||||
m_tempDirIn.remove();
|
||||
m_tempDirOut.remove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void nglcomp_test::writeTestFile(const QString &fn, const QByteArray &content) const
|
||||
{
|
||||
QFile f(subPathIn(fn));
|
||||
QVERIFY(f.open(QIODevice::WriteOnly));
|
||||
QVERIFY(f.write(content) == content.size());
|
||||
f.close();
|
||||
}
|
||||
|
||||
void nglcomp_test::checkContent(const QString &fn, const QByteArray &content) const
|
||||
{
|
||||
QFile f(subPathOut(QLatin1String("out/") + fn));
|
||||
QVERIFY(f.open(QIODevice::ReadOnly));
|
||||
QCOMPARE(f.readAll(), content);
|
||||
}
|
||||
|
||||
QString nglcomp_test::subPathIn(const QString &p) const
|
||||
{
|
||||
return m_tempDirIn.path() + QLatin1Char('/') + p;
|
||||
}
|
||||
|
||||
QString nglcomp_test::subPathOut(const QString &p) const
|
||||
{
|
||||
return m_tempDirOut.path() + QLatin1Char('/') + p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
NGLCoreApplication app(argc, argv);
|
||||
|
||||
app.createLogger("nglcomp_test", "log4cxx_nglcomp_test.props");
|
||||
nglcomp_test tc;
|
||||
return QTest::qExec(&tc, argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
class nglcomp_test : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
nglcomp_test() = default;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testCompressFile();
|
||||
void testCompressMemory();
|
||||
void testUncompressFile();
|
||||
void testUncompressMemory();
|
||||
void cleanupTestCase();
|
||||
private:
|
||||
void writeTestFile(const QString &fn, const QByteArray &content) const;
|
||||
void checkContent(const QString &fn, const QByteArray &content) const;
|
||||
QString subPathIn(const QString &p) const;
|
||||
QString subPathOut(const QString &p) const;
|
||||
private:
|
||||
QHash<QString, QByteArray> m_fileContent;
|
||||
QByteArray m_ba;
|
||||
QTemporaryFile m_tf;
|
||||
QTemporaryDir m_tempDirIn;
|
||||
QTemporaryDir m_tempDirOut;
|
||||
};
|
||||
Reference in new issue
Block a user