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;
 | 
						|
}
 |