Sort of working QuaZipDir

This commit is contained in:
alqualos 2012-09-08 07:52:51 +00:00
parent e1dad31427
commit 4e305010f0
7 changed files with 361 additions and 50 deletions

View file

@ -281,14 +281,7 @@ bool QuaZip::setCurrentFile(const QString& fileName, CaseSensitivity cs)
p->zipError=UNZ_PARAMERROR;
return false;
}
bool sens;
if(cs==csDefault) {
#ifdef Q_WS_WIN
sens=false;
#else
sens=true;
#endif
} else sens=cs==csSensitive;
bool sens = convertCaseSensitivity(cs) == Qt::CaseSensitive;
QString lower, current;
if(!sens) lower=fileName.toLower();
p->hasCurrentFile_f=false;
@ -546,3 +539,16 @@ QList<QuaZipFileInfo> QuaZip::getFileInfoList() const
else
return QList<QuaZipFileInfo>();
}
Qt::CaseSensitivity QuaZip::convertCaseSensitivity(QuaZip::CaseSensitivity cs)
{
if (cs == csDefault) {
#ifdef Q_WS_WIN
return Qt::CaseInsensitive;
#else
return Qt::CaseSensitive;
#endif
} else {
return cs == csSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
}
}

View file

@ -115,6 +115,7 @@ class QUAZIP_EXPORT QuaZip {
csSensitive=1, ///< Case sensitive.
csInsensitive=2 ///< Case insensitive.
};
static Qt::CaseSensitivity convertCaseSensitivity(CaseSensitivity);
private:
QuaZipPrivate *p;
// not (and will not be) implemented

View file

@ -15,9 +15,9 @@ private:
QDir::Filters filter;
QStringList nameFilters;
QDir::SortFlags sorting;
template<typename TFileInfo>
template<typename TFileInfoList>
bool entryInfoList(QStringList nameFilters, QDir::Filters filter,
QDir::SortFlags sort, QList<TFileInfo> *result) const;
QDir::SortFlags sort, TFileInfoList &result) const;
inline QString simplePath() const {return QDir::cleanPath(dir);}
};
@ -29,6 +29,8 @@ QuaZipDir::QuaZipDir(const QuaZipDir &that):
QuaZipDir::QuaZipDir(QuaZip *zip, const QString &dir):
d(new QuaZipDirPrivate(zip, dir))
{
if (d->dir == "/")
d->dir = "";
}
QuaZipDir::~QuaZipDir()
@ -56,12 +58,67 @@ QuaZip::CaseSensitivity QuaZipDir::caseSensitivity() const
return d->caseSensitivity;
}
bool QuaZipDir::cd(const QString &dirName)
bool QuaZipDir::cd(const QString &directoryName)
{
if (!exists(dirName))
return false;
d->dir = QDir::cleanPath(QDir(d->dir).filePath(dirName));
return true;
QString dirName = directoryName;
if (dirName.endsWith('/'))
dirName.chop(1);
if (dirName.contains('/')) {
if (dirName == "/") {
d->dir = "";
return true;
}
QuaZipDir dir(*this);
if (dirName.startsWith('/')) {
#ifdef QUAZIP_QUAZIPDIR_DEBUG
qDebug("QuaZipDir::cd(%s): going to /",
dirName.toUtf8().constData());
#endif
if (!dir.cd("/"))
return false;
}
QStringList path = dirName.split('/', QString::SkipEmptyParts);
for (QStringList::const_iterator i = path.constBegin();
i != path.end();
++i) {
const QString &step = *i;
#ifdef QUAZIP_QUAZIPDIR_DEBUG
qDebug("QuaZipDir::cd(%s): going to %s",
dirName.toUtf8().constData(),
step.toUtf8().constData());
#endif
if (!dir.cd(step))
return false;
}
d->dir = dir.path();
return true;
} else { // no '/'
if (dirName == ".") {
return true;
} else if (dirName == "..") {
if (isRoot()) {
return false;
} else {
int slashPos = d->dir.lastIndexOf('/');
if (slashPos == -1) {
d->dir = "";
} else {
d->dir = d->dir.left(slashPos);
}
return true;
}
} else { // a simple subdirectory
if (exists(dirName)) {
if (isRoot())
d->dir = dirName;
else
d->dir += "/" + dirName;
return true;
} else {
return false;
}
}
}
}
bool QuaZipDir::cdUp()
@ -79,12 +136,6 @@ QString QuaZipDir::dirName() const
return QDir(d->dir).dirName();
}
template<typename TFileInfo>
TFileInfo QuaZipDir_getFileInfo(QuaZip *zip, bool *ok,
const QString &relativeName,
bool isReal);
template<>
QuaZipFileInfo QuaZipDir_getFileInfo(QuaZip *zip, bool *ok,
const QString &relativeName,
bool isReal)
@ -93,6 +144,7 @@ QuaZipFileInfo QuaZipDir_getFileInfo(QuaZip *zip, bool *ok,
if (isReal) {
*ok = zip->getCurrentFileInfo(&info);
} else {
*ok = true;
info.compressedSize = 0;
info.crc = 0;
info.diskNumberStart = 0;
@ -107,13 +159,24 @@ QuaZipFileInfo QuaZipDir_getFileInfo(QuaZip *zip, bool *ok,
return info;
}
template<typename TFileInfoList>
void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, TFileInfoList &to);
template<>
QString QuaZipDir_getFileInfo(QuaZip *, bool *ok,
const QString &relativeName,
bool)
void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, QList<QuaZipFileInfo> &to)
{
*ok = true;
return relativeName;
to = from;
}
template<>
void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, QStringList &to)
{
to.clear();
for (QList<QuaZipFileInfo>::const_iterator i = from.constBegin();
i != from.constEnd();
++i) {
to.append(i->name);
}
}
// utility class to restore the current file
@ -130,15 +193,100 @@ private:
QString currentFile;
};
template<typename TFileInfo>
class QuaZipDirComparator
{
private:
QDir::SortFlags sort;
static QString getExtension(const QString &name);
int compareStrings(const QString &string1, const QString &string2);
public:
inline QuaZipDirComparator(QDir::SortFlags sort): sort(sort) {}
bool operator()(const QuaZipFileInfo &info1, const QuaZipFileInfo &info2);
};
QString QuaZipDirComparator::getExtension(const QString &name)
{
if (name.endsWith('.') || name.indexOf('.', 1) == -1) {
return "";
} else {
return name.mid(name.lastIndexOf('.') + 1);
}
}
int QuaZipDirComparator::compareStrings(const QString &string1,
const QString &string2)
{
if (sort & QDir::LocaleAware) {
if (sort & QDir::IgnoreCase) {
return string1.toLower().localeAwareCompare(string2.toLower());
} else {
return string1.localeAwareCompare(string2);
}
} else {
return string1.compare(string2, (sort & QDir::IgnoreCase)
? Qt::CaseInsensitive : Qt::CaseSensitive);
}
}
bool QuaZipDirComparator::operator()(const QuaZipFileInfo &info1,
const QuaZipFileInfo &info2)
{
QDir::SortFlags order = sort
& (QDir::Name | QDir::Time | QDir::Size | QDir::Type);
if ((sort & QDir::DirsFirst) == QDir::DirsFirst
|| (sort & QDir::DirsLast) == QDir::DirsLast) {
if (info1.name.endsWith('/') && !info2.name.endsWith('/'))
return (sort & QDir::DirsFirst) == QDir::DirsFirst;
else if (!info1.name.endsWith('/') && info2.name.endsWith('/'))
return (sort & QDir::DirsLast) == QDir::DirsLast;
}
bool result;
int extDiff;
switch (order) {
case QDir::Name:
result = compareStrings(info1.name, info2.name) < 0;
break;
case QDir::Type:
extDiff = compareStrings(getExtension(info1.name),
getExtension(info2.name));
if (extDiff == 0) {
result = compareStrings(info1.name, info2.name) < 0;
} else {
result = extDiff < 0;
}
break;
case QDir::Size:
if (info1.uncompressedSize == info2.uncompressedSize) {
result = compareStrings(info1.name, info2.name) < 0;
} else {
result = info1.uncompressedSize < info2.uncompressedSize;
}
break;
case QDir::Time:
if (info1.dateTime == info2.dateTime) {
result = compareStrings(info1.name, info2.name) < 0;
} else {
result = info1.dateTime < info2.dateTime;
}
break;
default:
qWarning("QuaZipDirComparator(): Invalid sort mode 0x%2X",
static_cast<unsigned>(sort));
return false;
}
return (sort & QDir::Reversed) ? !result : result;
}
template<typename TFileInfoList>
bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters,
QDir::Filters filter, QDir::SortFlags sort, QList<TFileInfo> *result) const
QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const
{
QString basePath = simplePath();
if (!basePath.isEmpty())
basePath += "/";
int baseLength = basePath.length();
result->clear();
result.clear();
QuaZipDirRestoreCurrent saveCurrent(zip);
if (!zip->goToFirstFile()) {
return zip->getZipError() == UNZ_OK;
@ -152,6 +300,7 @@ bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters,
if (nmfltr.isEmpty())
nmfltr = this->nameFilters;
QSet<QString> dirsFound;
QList<QuaZipFileInfo> list;
do {
QString name = zip->getCurrentFileName();
if (!name.startsWith(basePath))
@ -163,7 +312,7 @@ bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters,
int indexOfSlash = relativeName.indexOf('/');
// something like "subdir/"
isReal = indexOfSlash == relativeName.length() - 1;
relativeName = relativeName.left(indexOfSlash);
relativeName = relativeName.left(indexOfSlash + 1);
if (dirsFound.contains(relativeName))
continue;
isDir = true;
@ -176,13 +325,31 @@ bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters,
if (!nmfltr.isEmpty() && QDir::match(nmfltr, relativeName))
continue;
bool ok;
TFileInfo info = QuaZipDir_getFileInfo<TFileInfo>(zip, &ok, relativeName,
QuaZipFileInfo info = QuaZipDir_getFileInfo(zip, &ok, relativeName,
isReal);
if (!ok) {
return false;
}
result->append(info);
list.append(info);
} while (zip->goToNextFile());
QDir::SortFlags srt = sort;
if (srt == QDir::NoSort)
srt = sorting;
#ifdef QUAZIP_QUAZIPDIR_DEBUG
qDebug("QuaZipDirPrivate::entryInfoList(): before sort:");
foreach (QuaZipFileInfo info, list) {
qDebug("%s\t%s", info.name.toUtf8().constData(),
info.dateTime.toString(Qt::ISODate).toUtf8().constData());
}
#endif
if (srt != QDir::NoSort && (srt & QDir::Unsorted) != QDir::Unsorted) {
if (QuaZip::convertCaseSensitivity(caseSensitivity)
== Qt::CaseInsensitive)
srt |= QDir::IgnoreCase;
QuaZipDirComparator lessThan(srt);
qSort(list.begin(), list.end(), lessThan);
}
QuaZipDir_convertInfoList(list, result);
return true;
}
@ -190,7 +357,7 @@ QList<QuaZipFileInfo> QuaZipDir::entryInfoList(const QStringList &nameFilters,
QDir::Filters filters, QDir::SortFlags sort) const
{
QList<QuaZipFileInfo> result;
if (d->entryInfoList(nameFilters, filters, sort, &result))
if (d->entryInfoList(nameFilters, filters, sort, result))
return result;
else
return QList<QuaZipFileInfo>();
@ -206,7 +373,7 @@ QStringList QuaZipDir::entryList(const QStringList &nameFilters,
QDir::Filters filters, QDir::SortFlags sort) const
{
QStringList result;
if (d->entryInfoList(nameFilters, filters, sort, &result))
if (d->entryInfoList(nameFilters, filters, sort, result))
return result;
else
return QStringList();
@ -218,13 +385,49 @@ QStringList QuaZipDir::entryList(QDir::Filters filters,
return entryList(QStringList(), filters, sort);
}
bool QuaZipDir::exists(const QString &fileName) const
bool QuaZipDir::exists(const QString &filePath) const
{
QFileInfo fileInfo(filePath(fileName));
if (fileName == fileInfo.fileName()) {
return entryList(QDir::AllEntries, QDir::NoSort).contains(fileName);
QString fileName = filePath;
if (fileName.endsWith('/'))
fileName.chop(1);
if (fileName.contains('/')) {
if (fileName == "/")
return true;
QFileInfo fileInfo(fileName);
#ifdef QUAZIP_QUAZIPDIR_DEBUG
qDebug("QuaZipDir::exists(): fileName=%s, fileInfo.fileName()=%s, "
"fileInfo.path()=%s", fileName.toUtf8().constData(),
fileInfo.fileName().toUtf8().constData(),
fileInfo.path().toUtf8().constData());
#endif
QuaZipDir dir(*this);
return dir.cd(fileInfo.path()) && dir.exists(fileInfo.fileName());
} else {
return QuaZipDir(d->zip, fileInfo.path()).exists(fileInfo.fileName());
if (fileName == "..") {
return !isRoot();
} else if (fileName == ".") {
return true;
} else {
QStringList entries = entryList(QDir::AllEntries, QDir::NoSort);
#ifdef QUAZIP_QUAZIPDIR_DEBUG
qDebug("QuaZipDir::exists(): looking for %s",
fileName.toUtf8().constData());
for (QStringList::const_iterator i = entries.constBegin();
i != entries.constEnd();
++i) {
qDebug("QuaZipDir::exists(): entry: %s",
i->toUtf8().constData());
}
#endif
Qt::CaseSensitivity cs = QuaZip::convertCaseSensitivity(
d->caseSensitivity);
if (filePath.endsWith('/')) {
return entries.contains(filePath, cs);
} else {
return entries.contains(fileName, cs)
|| entries.contains(fileName + "/", cs);
}
}
}
}

View file

@ -89,6 +89,8 @@ class QuaZipFilePrivate {
{
zip=new QuaZip(zipName);
this->fileName=fileName;
if (this->fileName.startsWith('/'))
this->fileName = this->fileName.mid(1);
this->caseSensitivity=cs;
}
/// The constructor for the QuaZipFile constructor accepting a file name.
@ -200,6 +202,8 @@ void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs
return;
}
p->fileName=fileName;
if (p->fileName.startsWith('/'))
p->fileName = p->fileName.mid(1);
p->caseSensitivity=cs;
}

View file

@ -58,12 +58,21 @@ bool createTestArchive(const QString &zipName,
qWarning("Couldn't open %s", zipName.toUtf8().constData());
return false;
}
int i = 0;
QDateTime dt1;
foreach (QString fileName, fileNames) {
QuaZipFile zipFile(&zip);
QString filePath = QDir(dir).filePath(fileName);
QFileInfo fileInfo(filePath);
QuaZipNewInfo newInfo(fileName, filePath);
if (i == 0) // to test code that needs different timestamps
newInfo.dateTime = newInfo.dateTime.addSecs(-60);
else if (i == 1) // will use for the next file too
dt1 = newInfo.dateTime;
else if (i == 2) // to test identical timestamps
newInfo.dateTime = dt1;
if (!zipFile.open(QIODevice::WriteOnly,
QuaZipNewInfo(fileName, filePath), NULL, 0,
newInfo, NULL, 0,
fileInfo.isDir() ? 0 : 8)) {
qWarning("Couldn't open %s in %s", fileName.toUtf8()
.constData(), zipName.toUtf8().constData());
@ -94,6 +103,7 @@ bool createTestArchive(const QString &zipName,
file.close();
}
zipFile.close();
++i;
}
zip.setComment(QString("This is the %1 archive").arg(zipName));
zip.close();

View file

@ -11,24 +11,54 @@ void TestQuaZipDir::entryList_data()
QTest::addColumn<QString>("dirName");
// QDir::Filters type breaks Qt meta type system on MSVC
QTest::addColumn<int>("filter");
QTest::addColumn<int>("sort");
QTest::addColumn<QStringList>("entries");
QTest::newRow("simple") << "simple.zip" << (
QStringList() << "test0.txt" << "testdir1/test1.txt"
<< "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt")
<< "testdir2" << static_cast<int>(QDir::NoFilter) <<
(QStringList() << "test2.txt" << "subdir");
<< "testdir2" << static_cast<int>(QDir::NoFilter)
<< static_cast<int>(QDir::Unsorted)
<< (QStringList() << "test2.txt" << "subdir/");
QTest::newRow("separate dir") << "sepdir.zip" << (
QStringList() << "laj/" << "laj/lajfile.txt")
<< "" << static_cast<int>(QDir::NoFilter)
<< (QStringList() << "laj");
<< static_cast<int>(QDir::Unsorted)
<< (QStringList() << "laj/");
QTest::newRow("dirs only") << "dirsonly.zip" << (
QStringList() << "file" << "dir/")
<< "" << static_cast<int>(QDir::Dirs)
<< (QStringList() << "dir");
<< static_cast<int>(QDir::Unsorted)
<< (QStringList() << "dir/");
QTest::newRow("files only") << "filesonly.zip" << (
QStringList() << "file1" << "parent/dir/" << "parent/file2")
<< "parent" << static_cast<int>(QDir::Files)
<< static_cast<int>(QDir::Unsorted)
<< (QStringList() << "file2");
QTest::newRow("sorted") << "sorted.zip" << (
QStringList() << "file1" << "parent/subdir/" << "parent/subdir2/file3" << "parent/file2" << "parent/file0")
<< "parent" << static_cast<int>(QDir::NoFilter)
<< static_cast<int>(QDir::Name)
<< (QStringList() << "file0" << "file2" << "subdir/" << "subdir2/");
QTest::newRow("sorted dirs first") << "sorted-dirs.zip" << (
QStringList() << "file1" << "parent/subdir/" << "parent/subdir2/file3" << "parent/file2" << "parent/file0")
<< "parent" << static_cast<int>(QDir::NoFilter)
<< static_cast<int>(QDir::Name | QDir::DirsFirst)
<< (QStringList() << "subdir/" << "subdir2/" << "file0" << "file2");
QTest::newRow("sorted dirs first reversed") << "sorted-reverse.zip" << (
QStringList() << "file1" << "parent/subdir/" << "parent/subdir2/file3" << "parent/file2" << "parent/file0")
<< "parent" << static_cast<int>(QDir::NoFilter)
<< static_cast<int>(QDir::Name | QDir::DirsFirst | QDir::Reversed)
<< (QStringList() << "subdir2/" << "subdir/" << "file2" << "file0");
QTest::newRow("sorted by size") << "sorted-size.zip" << (
QStringList() << "file000" << "file10")
<< "/" << static_cast<int>(QDir::NoFilter)
<< static_cast<int>(QDir::Size)
<< (QStringList() << "file10" << "file000");
QTest::newRow("sorted by time") << "sorted-time.zip" << (
QStringList() << "file04" << "file03" << "file02" << "subdir/subfile")
<< "/" << static_cast<int>(QDir::NoFilter)
<< static_cast<int>(QDir::Time)
<< (QStringList() << "subdir/" << "file04" << "file02" << "file03");
}
void TestQuaZipDir::entryList()
@ -37,24 +67,79 @@ void TestQuaZipDir::entryList()
QFETCH(QStringList, fileNames);
QFETCH(QString, dirName);
QFETCH(int, filter);
QFETCH(int, sort);
QDir::Filters filters = static_cast<QDir::Filters>(filter);
QDir::SortFlags sorting = static_cast<QDir::SortFlags>(sort);
QFETCH(QStringList, entries);
QDir curDir;
if (!curDir.mkpath("jlext/jldir")) {
QFAIL("Couldn't mkpath jlext/jldir");
}
if (!createTestFiles(fileNames)) {
QFAIL("Couldn't create test files");
}
if (!createTestArchive(zipName, fileNames)) {
QFAIL("Couldn't create test archive");
}
removeTestFiles(fileNames);
QuaZip zip(zipName);
QVERIFY(zip.open(QuaZip::mdUnzip));
QuaZipDir dir(&zip, dirName);
QCOMPARE(dir.entryList(filters), entries);
QCOMPARE(dir.entryList(filters, sorting), entries);
zip.close();
curDir.remove(zipName);
}
void TestQuaZipDir::cd_data()
{
QTest::addColumn<QString>("zipName");
QTest::addColumn<QStringList>("fileNames");
QTest::addColumn<QString>("dirName");
QTest::addColumn<QString>("targetDirName");
QTest::addColumn<QString>("result");
QTest::newRow("cdDown") << "simple.zip" << (
QStringList() << "cddown.txt" << "testdir1/test1.txt"
<< "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt")
<< "" << "testdir1" << "testdir1";
QTest::newRow("cdUp") << "cdup.zip" << (
QStringList() << "test0.txt" << "testdir1/test1.txt"
<< "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt")
<< "testdir1" << ".." << "";
QTest::newRow("cdSide") << "cdside.zip" << (
QStringList() << "test0.txt" << "testdir1/test1.txt"
<< "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt")
<< "testdir1" << "../testdir2" << "testdir2";
QTest::newRow("cdDownUp") << "cdside.zip" << (
QStringList() << "test0.txt" << "testdir1/test1.txt"
<< "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt")
<< "" << "testdir1/.." << "";
QTest::newRow("cdDeep") << "cdside.zip" << (
QStringList() << "test0.txt" << "testdir1/test1.txt"
<< "testdir2/test2.txt" << "testdir2/subdir/test2sub.txt")
<< "" << "testdir2/subdir" << "testdir2/subdir";
QTest::newRow("cdDeeper") << "cdside.zip" << (
QStringList() << "test0.txt" << "testdir1/test1.txt"
<< "testdir2/test2.txt" << "testdir2/subdir/subdir2/subdir3/test2sub.txt")
<< "testdir2/subdir" << "subdir2/subdir3" << "testdir2/subdir/subdir2/subdir3";
}
void TestQuaZipDir::cd()
{
QFETCH(QString, zipName);
QFETCH(QStringList, fileNames);
QFETCH(QString, dirName);
QFETCH(QString, targetDirName);
QFETCH(QString, result);
QDir curDir;
if (!createTestFiles(fileNames)) {
QFAIL("Couldn't create test files");
}
if (!createTestArchive(zipName, fileNames)) {
QFAIL("Couldn't create test archive");
}
removeTestFiles(fileNames);
QuaZip zip(zipName);
QVERIFY(zip.open(QuaZip::mdUnzip));
QuaZipDir dir(&zip, dirName);
QVERIFY(dir.cd(targetDirName));
QCOMPARE(dir.path(), result);
zip.close();
curDir.rmpath("jlext/jldir");
removeTestFiles(fileNames);
curDir.remove(zipName);
}

View file

@ -8,6 +8,8 @@ class TestQuaZipDir: public QObject {
private slots:
void entryList_data();
void entryList();
void cd_data();
void cd();
};
#endif // QUAZIP_TEST_QUAZIPDIR_H