253067c782
Also, implemented some basic modlist logic, to be wired up.
508 lines
14 KiB
C++
508 lines
14 KiB
C++
#include "quazipdir.h"
|
|
|
|
#include <QSet>
|
|
#include <QSharedData>
|
|
|
|
class QuaZipDirPrivate: public QSharedData {
|
|
friend class QuaZipDir;
|
|
private:
|
|
QuaZipDirPrivate(QuaZip *zip, const QString &dir = QString()):
|
|
zip(zip), dir(dir), caseSensitivity(QuaZip::csDefault),
|
|
filter(QDir::NoFilter), sorting(QDir::NoSort) {}
|
|
QuaZip *zip;
|
|
QString dir;
|
|
QuaZip::CaseSensitivity caseSensitivity;
|
|
QDir::Filters filter;
|
|
QStringList nameFilters;
|
|
QDir::SortFlags sorting;
|
|
template<typename TFileInfoList>
|
|
bool entryInfoList(QStringList nameFilters, QDir::Filters filter,
|
|
QDir::SortFlags sort, TFileInfoList &result) const;
|
|
inline QString simplePath() const {return QDir::cleanPath(dir);}
|
|
};
|
|
|
|
QuaZipDir::QuaZipDir(const QuaZipDir &that):
|
|
d(that.d)
|
|
{
|
|
}
|
|
|
|
QuaZipDir::QuaZipDir(QuaZip *zip, const QString &dir):
|
|
d(new QuaZipDirPrivate(zip, dir))
|
|
{
|
|
if (d->dir.startsWith('/'))
|
|
d->dir = d->dir.mid(1);
|
|
}
|
|
|
|
QuaZipDir::~QuaZipDir()
|
|
{
|
|
}
|
|
|
|
bool QuaZipDir::operator==(const QuaZipDir &that)
|
|
{
|
|
return d->zip == that.d->zip && d->dir == that.d->dir;
|
|
}
|
|
|
|
QuaZipDir& QuaZipDir::operator=(const QuaZipDir &that)
|
|
{
|
|
this->d = that.d;
|
|
return *this;
|
|
}
|
|
|
|
QString QuaZipDir::operator[](int pos) const
|
|
{
|
|
return entryList().at(pos);
|
|
}
|
|
|
|
QuaZip::CaseSensitivity QuaZipDir::caseSensitivity() const
|
|
{
|
|
return d->caseSensitivity;
|
|
}
|
|
|
|
bool QuaZipDir::cd(const QString &directoryName)
|
|
{
|
|
if (directoryName == "/") {
|
|
d->dir = "";
|
|
return true;
|
|
}
|
|
QString dirName = directoryName;
|
|
if (dirName.endsWith('/'))
|
|
dirName.chop(1);
|
|
if (dirName.contains('/')) {
|
|
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()
|
|
{
|
|
return cd("..");
|
|
}
|
|
|
|
uint QuaZipDir::count() const
|
|
{
|
|
return entryList().count();
|
|
}
|
|
|
|
QString QuaZipDir::dirName() const
|
|
{
|
|
return QDir(d->dir).dirName();
|
|
}
|
|
|
|
QuaZipFileInfo QuaZipDir_getFileInfo(QuaZip *zip, bool *ok,
|
|
const QString &relativeName,
|
|
bool isReal)
|
|
{
|
|
QuaZipFileInfo info;
|
|
if (isReal) {
|
|
*ok = zip->getCurrentFileInfo(&info);
|
|
} else {
|
|
*ok = true;
|
|
info.compressedSize = 0;
|
|
info.crc = 0;
|
|
info.diskNumberStart = 0;
|
|
info.externalAttr = 0;
|
|
info.flags = 0;
|
|
info.internalAttr = 0;
|
|
info.method = 0;
|
|
info.uncompressedSize = 0;
|
|
info.versionCreated = info.versionNeeded = 0;
|
|
}
|
|
info.name = relativeName;
|
|
return info;
|
|
}
|
|
|
|
template<typename TFileInfoList>
|
|
void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, TFileInfoList &to);
|
|
|
|
template<>
|
|
void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo> &from, QList<QuaZipFileInfo> &to)
|
|
{
|
|
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
|
|
class QuaZipDirRestoreCurrent {
|
|
public:
|
|
inline QuaZipDirRestoreCurrent(QuaZip *zip):
|
|
zip(zip), currentFile(zip->getCurrentFileName()) {}
|
|
inline ~QuaZipDirRestoreCurrent()
|
|
{
|
|
zip->setCurrentFile(currentFile);
|
|
}
|
|
private:
|
|
QuaZip *zip;
|
|
QString currentFile;
|
|
};
|
|
|
|
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, TFileInfoList &result) const
|
|
{
|
|
QString basePath = simplePath();
|
|
if (!basePath.isEmpty())
|
|
basePath += "/";
|
|
int baseLength = basePath.length();
|
|
result.clear();
|
|
QuaZipDirRestoreCurrent saveCurrent(zip);
|
|
if (!zip->goToFirstFile()) {
|
|
return zip->getZipError() == UNZ_OK;
|
|
}
|
|
QDir::Filters fltr = filter;
|
|
if (fltr == QDir::NoFilter)
|
|
fltr = this->filter;
|
|
if (fltr == QDir::NoFilter)
|
|
fltr = QDir::AllEntries;
|
|
QStringList nmfltr = nameFilters;
|
|
if (nmfltr.isEmpty())
|
|
nmfltr = this->nameFilters;
|
|
QSet<QString> dirsFound;
|
|
QList<QuaZipFileInfo> list;
|
|
do {
|
|
QString name = zip->getCurrentFileName();
|
|
if (!name.startsWith(basePath))
|
|
continue;
|
|
QString relativeName = name.mid(baseLength);
|
|
bool isDir = false;
|
|
bool isReal = true;
|
|
if (relativeName.contains('/')) {
|
|
int indexOfSlash = relativeName.indexOf('/');
|
|
// something like "subdir/"
|
|
isReal = indexOfSlash == relativeName.length() - 1;
|
|
relativeName = relativeName.left(indexOfSlash + 1);
|
|
if (dirsFound.contains(relativeName))
|
|
continue;
|
|
isDir = true;
|
|
}
|
|
dirsFound.insert(relativeName);
|
|
if ((fltr & QDir::Dirs) == 0 && isDir)
|
|
continue;
|
|
if ((fltr & QDir::Files) == 0 && !isDir)
|
|
continue;
|
|
if (!nmfltr.isEmpty() && QDir::match(nmfltr, relativeName))
|
|
continue;
|
|
bool ok;
|
|
QuaZipFileInfo info = QuaZipDir_getFileInfo(zip, &ok, relativeName,
|
|
isReal);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
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;
|
|
}
|
|
|
|
QList<QuaZipFileInfo> QuaZipDir::entryInfoList(const QStringList &nameFilters,
|
|
QDir::Filters filters, QDir::SortFlags sort) const
|
|
{
|
|
QList<QuaZipFileInfo> result;
|
|
if (d->entryInfoList(nameFilters, filters, sort, result))
|
|
return result;
|
|
else
|
|
return QList<QuaZipFileInfo>();
|
|
}
|
|
|
|
QList<QuaZipFileInfo> QuaZipDir::entryInfoList(QDir::Filters filters,
|
|
QDir::SortFlags sort) const
|
|
{
|
|
return entryInfoList(QStringList(), filters, sort);
|
|
}
|
|
|
|
QStringList QuaZipDir::entryList(const QStringList &nameFilters,
|
|
QDir::Filters filters, QDir::SortFlags sort) const
|
|
{
|
|
QStringList result;
|
|
if (d->entryInfoList(nameFilters, filters, sort, result))
|
|
return result;
|
|
else
|
|
return QStringList();
|
|
}
|
|
|
|
QStringList QuaZipDir::entryList(QDir::Filters filters,
|
|
QDir::SortFlags sort) const
|
|
{
|
|
return entryList(QStringList(), filters, sort);
|
|
}
|
|
|
|
bool QuaZipDir::exists(const QString &filePath) const
|
|
{
|
|
if (filePath == "/")
|
|
return true;
|
|
QString fileName = filePath;
|
|
if (fileName.endsWith('/'))
|
|
fileName.chop(1);
|
|
if (fileName.contains('/')) {
|
|
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 {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool QuaZipDir::exists() const
|
|
{
|
|
QDir thisDir(d->dir);
|
|
return QuaZipDir(d->zip, thisDir.filePath("..")).exists(thisDir.dirName());
|
|
}
|
|
|
|
QString QuaZipDir::filePath(const QString &fileName) const
|
|
{
|
|
return QDir(d->dir).filePath(fileName);
|
|
}
|
|
|
|
QDir::Filters QuaZipDir::filter()
|
|
{
|
|
return d->filter;
|
|
}
|
|
|
|
bool QuaZipDir::isRoot() const
|
|
{
|
|
return d->simplePath().isEmpty();
|
|
}
|
|
|
|
QStringList QuaZipDir::nameFilters() const
|
|
{
|
|
return d->nameFilters;
|
|
}
|
|
|
|
QString QuaZipDir::path() const
|
|
{
|
|
return d->dir;
|
|
}
|
|
|
|
QString QuaZipDir::relativeFilePath(const QString &fileName) const
|
|
{
|
|
return QDir(d->dir).relativeFilePath(fileName);
|
|
}
|
|
|
|
void QuaZipDir::setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity)
|
|
{
|
|
d->caseSensitivity = caseSensitivity;
|
|
}
|
|
|
|
void QuaZipDir::setFilter(QDir::Filters filters)
|
|
{
|
|
d->filter = filters;
|
|
}
|
|
|
|
void QuaZipDir::setNameFilters(const QStringList &nameFilters)
|
|
{
|
|
d->nameFilters = nameFilters;
|
|
}
|
|
|
|
void QuaZipDir::setPath(const QString &path)
|
|
{
|
|
QString newDir = path;
|
|
if (newDir == "/") {
|
|
d->dir = "";
|
|
} else {
|
|
if (newDir.endsWith('/'))
|
|
newDir.chop(1);
|
|
if (newDir.startsWith('/'))
|
|
newDir = newDir.mid(1);
|
|
d->dir = newDir;
|
|
}
|
|
}
|
|
|
|
void QuaZipDir::setSorting(QDir::SortFlags sort)
|
|
{
|
|
d->sorting = sort;
|
|
}
|
|
|
|
QDir::SortFlags QuaZipDir::sorting() const
|
|
{
|
|
return d->sorting;
|
|
}
|