File indexing completed on 2025-01-26 04:24:53

0001 /*
0002 Copyright (C) 2005-2014 Sergey A. Tachenov
0003 
0004 This file is part of QuaZIP.
0005 
0006 QuaZIP is free software: you can redistribute it and/or modify
0007 it under the terms of the GNU Lesser General Public License as published by
0008 the Free Software Foundation, either version 2.1 of the License, or
0009 (at your option) any later version.
0010 
0011 QuaZIP is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU Lesser General Public License for more details.
0015 
0016 You should have received a copy of the GNU Lesser General Public License
0017 along with QuaZIP.  If not, see <http://www.gnu.org/licenses/>.
0018 
0019 See COPYING file for the full LGPL text.
0020 
0021 Original ZIP package is copyrighted by Gilles Vollant and contributors,
0022 see quazip/(un)zip.h files for details. Basically it's the zlib license.
0023 */
0024 
0025 #include "quazipdir.h"
0026 
0027 #include <QSet>
0028 #include <QSharedData>
0029 
0030 /// \cond internal
0031 class QuaZipDirPrivate: public QSharedData {
0032     friend class QuaZipDir;
0033 private:
0034     QuaZipDirPrivate(QuaZip *zip, const QString &dir = QString()):
0035         zip(zip), dir(dir), caseSensitivity(QuaZip::csDefault),
0036         filter(QDir::NoFilter), sorting(QDir::NoSort) {}
0037     QuaZip *zip;
0038     QString dir;
0039     QuaZip::CaseSensitivity caseSensitivity;
0040     QDir::Filters filter;
0041     QStringList nameFilters;
0042     QDir::SortFlags sorting;
0043     template<typename TFileInfoList>
0044     bool entryInfoList(QStringList nameFilters, QDir::Filters filter,
0045         QDir::SortFlags sort, TFileInfoList &result) const;
0046     inline QString simplePath() const {return QDir::cleanPath(dir);}
0047 };
0048 /// \endcond
0049 
0050 QuaZipDir::QuaZipDir(const QuaZipDir &that):
0051     d(that.d)
0052 {
0053 }
0054 
0055 QuaZipDir::QuaZipDir(QuaZip *zip, const QString &dir):
0056     d(new QuaZipDirPrivate(zip, dir))
0057 {
0058     if (d->dir.startsWith('/'))
0059         d->dir = d->dir.mid(1);
0060 }
0061 
0062 QuaZipDir::~QuaZipDir()
0063 {
0064 }
0065 
0066 bool QuaZipDir::operator==(const QuaZipDir &that)
0067 {
0068     return d->zip == that.d->zip && d->dir == that.d->dir;
0069 }
0070 
0071 QuaZipDir& QuaZipDir::operator=(const QuaZipDir &that)
0072 {
0073     this->d = that.d;
0074     return *this;
0075 }
0076 
0077 QString QuaZipDir::operator[](int pos) const
0078 {
0079     return entryList().at(pos);
0080 }
0081 
0082 QuaZip::CaseSensitivity QuaZipDir::caseSensitivity() const
0083 {
0084     return d->caseSensitivity;
0085 }
0086 
0087 bool QuaZipDir::cd(const QString &directoryName)
0088 {
0089     if (directoryName == "/") {
0090         d->dir = "";
0091         return true;
0092     }
0093     QString dirName = directoryName;
0094     if (dirName.endsWith('/'))
0095         dirName.chop(1);
0096     if (dirName.contains('/')) {
0097         QuaZipDir dir(*this);
0098         if (dirName.startsWith('/')) {
0099 #ifdef QUAZIP_QUAZIPDIR_DEBUG
0100             qDebug("QuaZipDir::cd(%s): going to /",
0101                     dirName.toUtf8().constData());
0102 #endif
0103             if (!dir.cd("/"))
0104                 return false;
0105         }
0106         QStringList path = dirName.split('/', QString::SkipEmptyParts);
0107         for (QStringList::const_iterator i = path.constBegin();
0108                 i != path.end();
0109                 ++i) {
0110             const QString &step = *i;
0111 #ifdef QUAZIP_QUAZIPDIR_DEBUG
0112             qDebug("QuaZipDir::cd(%s): going to %s",
0113                     dirName.toUtf8().constData(),
0114                     step.toUtf8().constData());
0115 #endif
0116             if (!dir.cd(step))
0117                 return false;
0118         }
0119         d->dir = dir.path();
0120         return true;
0121     } else { // no '/'
0122         if (dirName == ".") {
0123             return true;
0124         } else if (dirName == "..") {
0125             if (isRoot()) {
0126                 return false;
0127             } else {
0128                 int slashPos = d->dir.lastIndexOf('/');
0129                 if (slashPos == -1) {
0130                     d->dir = "";
0131                 } else {
0132                     d->dir = d->dir.left(slashPos);
0133                 }
0134                 return true;
0135             }
0136         } else { // a simple subdirectory
0137             if (exists(dirName)) {
0138                 if (isRoot())
0139                     d->dir = dirName;
0140                 else
0141                     d->dir += "/" + dirName;
0142                 return true;
0143             } else {
0144                 return false;
0145             }
0146         }
0147     }
0148 }
0149 
0150 bool QuaZipDir::cdUp()
0151 {
0152     return cd("..");
0153 }
0154 
0155 uint QuaZipDir::count() const
0156 {
0157     return entryList().count();
0158 }
0159 
0160 QString QuaZipDir::dirName() const
0161 {
0162     return QDir(d->dir).dirName();
0163 }
0164 
0165 QuaZipFileInfo64 QuaZipDir_getFileInfo(QuaZip *zip, bool *ok,
0166                                   const QString &relativeName,
0167                                   bool isReal)
0168 {
0169     QuaZipFileInfo64 info;
0170     if (isReal) {
0171         *ok = zip->getCurrentFileInfo(&info);
0172     } else {
0173         *ok = true;
0174         info.compressedSize = 0;
0175         info.crc = 0;
0176         info.diskNumberStart = 0;
0177         info.externalAttr = 0;
0178         info.flags = 0;
0179         info.internalAttr = 0;
0180         info.method = 0;
0181         info.uncompressedSize = 0;
0182         info.versionCreated = info.versionNeeded = 0;
0183     }
0184     info.name = relativeName;
0185     return info;
0186 }
0187 
0188 static void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo64> &from,
0189                                       QList<QuaZipFileInfo64> &to)
0190 {
0191     to = from;
0192 }
0193 
0194 static void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo64> &from,
0195                                       QStringList &to)
0196 {
0197     to.clear();
0198     for (QList<QuaZipFileInfo64>::const_iterator i = from.constBegin();
0199             i != from.constEnd();
0200             ++i) {
0201         to.append(i->name);
0202     }
0203 }
0204 
0205 static void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo64> &from,
0206                                       QList<QuaZipFileInfo> &to)
0207 {
0208     to.clear();
0209     for (QList<QuaZipFileInfo64>::const_iterator i = from.constBegin();
0210             i != from.constEnd();
0211             ++i) {
0212         QuaZipFileInfo info32;
0213         i->toQuaZipFileInfo(info32);
0214         to.append(info32);
0215     }
0216 }
0217 
0218 /// \cond internal
0219 /**
0220   An utility class to restore the current file.
0221   */
0222 class QuaZipDirRestoreCurrent {
0223 public:
0224     inline QuaZipDirRestoreCurrent(QuaZip *zip):
0225         zip(zip), currentFile(zip->getCurrentFileName()) {}
0226     inline ~QuaZipDirRestoreCurrent()
0227     {
0228         zip->setCurrentFile(currentFile);
0229     }
0230 private:
0231     QuaZip *zip;
0232     QString currentFile;
0233 };
0234 /// \endcond
0235 
0236 /// \cond internal
0237 class QuaZipDirComparator
0238 {
0239     private:
0240         QDir::SortFlags sort;
0241         static QString getExtension(const QString &name);
0242         int compareStrings(const QString &string1, const QString &string2);
0243     public:
0244         inline QuaZipDirComparator(QDir::SortFlags sort): sort(sort) {}
0245         bool operator()(const QuaZipFileInfo64 &info1, const QuaZipFileInfo64 &info2);
0246 };
0247 
0248 QString QuaZipDirComparator::getExtension(const QString &name)
0249 {
0250     if (name.endsWith('.') || name.indexOf('.', 1) == -1) {
0251         return "";
0252     } else {
0253         return name.mid(name.lastIndexOf('.') + 1);
0254     }
0255 
0256 }
0257 
0258 int QuaZipDirComparator::compareStrings(const QString &string1,
0259         const QString &string2)
0260 {
0261     if (sort & QDir::LocaleAware) {
0262         if (sort & QDir::IgnoreCase) {
0263             return string1.toLower().localeAwareCompare(string2.toLower());
0264         } else {
0265             return string1.localeAwareCompare(string2);
0266         }
0267     } else {
0268         return string1.compare(string2, (sort & QDir::IgnoreCase)
0269                 ? Qt::CaseInsensitive : Qt::CaseSensitive);
0270     }
0271 }
0272 
0273 bool QuaZipDirComparator::operator()(const QuaZipFileInfo64 &info1,
0274         const QuaZipFileInfo64 &info2)
0275 {
0276     QDir::SortFlags order = sort
0277         & (QDir::Name | QDir::Time | QDir::Size | QDir::Type);
0278     if ((sort & QDir::DirsFirst) == QDir::DirsFirst
0279             || (sort & QDir::DirsLast) == QDir::DirsLast) {
0280         if (info1.name.endsWith('/') && !info2.name.endsWith('/'))
0281             return (sort & QDir::DirsFirst) == QDir::DirsFirst;
0282         else if (!info1.name.endsWith('/') && info2.name.endsWith('/'))
0283             return (sort & QDir::DirsLast) == QDir::DirsLast;
0284     }
0285     bool result;
0286     int extDiff;
0287     switch (order) {
0288         case QDir::Name:
0289             result = compareStrings(info1.name, info2.name) < 0;
0290             break;
0291         case QDir::Type:
0292             extDiff = compareStrings(getExtension(info1.name),
0293                     getExtension(info2.name));
0294             if (extDiff == 0) {
0295                 result = compareStrings(info1.name, info2.name) < 0;
0296             } else {
0297                 result = extDiff < 0;
0298             }
0299             break;
0300         case QDir::Size:
0301             if (info1.uncompressedSize == info2.uncompressedSize) {
0302                 result = compareStrings(info1.name, info2.name) < 0;
0303             } else {
0304                 result = info1.uncompressedSize < info2.uncompressedSize;
0305             }
0306             break;
0307         case QDir::Time:
0308             if (info1.dateTime == info2.dateTime) {
0309                 result = compareStrings(info1.name, info2.name) < 0;
0310             } else {
0311                 result = info1.dateTime < info2.dateTime;
0312             }
0313             break;
0314         default:
0315             qWarning("QuaZipDirComparator(): Invalid sort mode 0x%2X",
0316                     static_cast<unsigned>(sort));
0317             return false;
0318     }
0319     return (sort & QDir::Reversed) ? !result : result;
0320 }
0321 
0322 template<typename TFileInfoList>
0323 bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters, 
0324     QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const
0325 {
0326     QString basePath = simplePath();
0327     if (!basePath.isEmpty())
0328         basePath += "/";
0329     int baseLength = basePath.length();
0330     result.clear();
0331     QuaZipDirRestoreCurrent saveCurrent(zip);
0332     if (!zip->goToFirstFile()) {
0333         return zip->getZipError() == UNZ_OK;
0334     }
0335     QDir::Filters fltr = filter;
0336     if (fltr == QDir::NoFilter)
0337         fltr = this->filter;
0338     if (fltr == QDir::NoFilter)
0339         fltr = QDir::AllEntries;
0340     QStringList nmfltr = nameFilters;
0341     if (nmfltr.isEmpty())
0342         nmfltr = this->nameFilters;
0343     QSet<QString> dirsFound;
0344     QList<QuaZipFileInfo64> list;
0345     do {
0346         QString name = zip->getCurrentFileName();
0347         if (!name.startsWith(basePath))
0348             continue;
0349         QString relativeName = name.mid(baseLength);
0350         if (relativeName.isEmpty())
0351             continue;
0352         bool isDir = false;
0353         bool isReal = true;
0354         if (relativeName.contains('/')) {
0355             int indexOfSlash = relativeName.indexOf('/');
0356             // something like "subdir/"
0357             isReal = indexOfSlash == relativeName.length() - 1;
0358             relativeName = relativeName.left(indexOfSlash + 1);
0359             if (dirsFound.contains(relativeName))
0360                 continue;
0361             isDir = true;
0362         }
0363         dirsFound.insert(relativeName);
0364         if ((fltr & QDir::Dirs) == 0 && isDir)
0365             continue;
0366         if ((fltr & QDir::Files) == 0 && !isDir)
0367             continue;
0368         if (!nmfltr.isEmpty() && !QDir::match(nmfltr, relativeName))
0369             continue;
0370         bool ok;
0371         QuaZipFileInfo64 info = QuaZipDir_getFileInfo(zip, &ok, relativeName,
0372             isReal);
0373         if (!ok) {
0374             return false;
0375         }
0376         list.append(info);
0377     } while (zip->goToNextFile());
0378     QDir::SortFlags srt = sort;
0379     if (srt == QDir::NoSort)
0380         srt = sorting;
0381 #ifdef QUAZIP_QUAZIPDIR_DEBUG
0382     qDebug("QuaZipDirPrivate::entryInfoList(): before sort:");
0383     foreach (QuaZipFileInfo64 info, list) {
0384         qDebug("%s\t%s", info.name.toUtf8().constData(),
0385                 info.dateTime.toString(Qt::ISODate).toUtf8().constData());
0386     }
0387 #endif
0388     if (srt != QDir::NoSort && (srt & QDir::Unsorted) != QDir::Unsorted) {
0389         if (QuaZip::convertCaseSensitivity(caseSensitivity)
0390                 == Qt::CaseInsensitive)
0391             srt |= QDir::IgnoreCase;
0392         QuaZipDirComparator lessThan(srt);
0393         qSort(list.begin(), list.end(), lessThan);
0394     }
0395     QuaZipDir_convertInfoList(list, result);
0396     return true;
0397 }
0398 
0399 /// \endcond
0400 
0401 QList<QuaZipFileInfo> QuaZipDir::entryInfoList(const QStringList &nameFilters,
0402     QDir::Filters filters, QDir::SortFlags sort) const
0403 {
0404     QList<QuaZipFileInfo> result;
0405     if (d->entryInfoList(nameFilters, filters, sort, result))
0406         return result;
0407     else
0408         return QList<QuaZipFileInfo>();
0409 }
0410 
0411 QList<QuaZipFileInfo> QuaZipDir::entryInfoList(QDir::Filters filters,
0412     QDir::SortFlags sort) const
0413 {
0414     return entryInfoList(QStringList(), filters, sort);
0415 }
0416 
0417 QList<QuaZipFileInfo64> QuaZipDir::entryInfoList64(const QStringList &nameFilters,
0418     QDir::Filters filters, QDir::SortFlags sort) const
0419 {
0420     QList<QuaZipFileInfo64> result;
0421     if (d->entryInfoList(nameFilters, filters, sort, result))
0422         return result;
0423     else
0424         return QList<QuaZipFileInfo64>();
0425 }
0426 
0427 QList<QuaZipFileInfo64> QuaZipDir::entryInfoList64(QDir::Filters filters,
0428     QDir::SortFlags sort) const
0429 {
0430     return entryInfoList64(QStringList(), filters, sort);
0431 }
0432 
0433 QStringList QuaZipDir::entryList(const QStringList &nameFilters,
0434     QDir::Filters filters, QDir::SortFlags sort) const
0435 {
0436     QStringList result;
0437     if (d->entryInfoList(nameFilters, filters, sort, result))
0438         return result;
0439     else
0440         return QStringList();
0441 }
0442 
0443 QStringList QuaZipDir::entryList(QDir::Filters filters,
0444     QDir::SortFlags sort) const
0445 {
0446     return entryList(QStringList(), filters, sort);
0447 }
0448 
0449 bool QuaZipDir::exists(const QString &filePath) const
0450 {
0451     if (filePath == "/" || filePath.isEmpty())
0452         return true;
0453     QString fileName = filePath;
0454     if (fileName.endsWith('/'))
0455         fileName.chop(1);
0456     if (fileName.contains('/')) {
0457         QFileInfo fileInfo(fileName);
0458 #ifdef QUAZIP_QUAZIPDIR_DEBUG
0459         qDebug("QuaZipDir::exists(): fileName=%s, fileInfo.fileName()=%s, "
0460                 "fileInfo.path()=%s", fileName.toUtf8().constData(),
0461                 fileInfo.fileName().toUtf8().constData(),
0462                 fileInfo.path().toUtf8().constData());
0463 #endif
0464         QuaZipDir dir(*this);
0465         return dir.cd(fileInfo.path()) && dir.exists(fileInfo.fileName());
0466     } else {
0467         if (fileName == "..") {
0468             return !isRoot();
0469         } else if (fileName == ".") {
0470             return true;
0471         } else {
0472             QStringList entries = entryList(QDir::AllEntries, QDir::NoSort);
0473 #ifdef QUAZIP_QUAZIPDIR_DEBUG
0474             qDebug("QuaZipDir::exists(): looking for %s",
0475                     fileName.toUtf8().constData());
0476             for (QStringList::const_iterator i = entries.constBegin();
0477                     i != entries.constEnd();
0478                     ++i) {
0479                 qDebug("QuaZipDir::exists(): entry: %s",
0480                         i->toUtf8().constData());
0481             }
0482 #endif
0483             Qt::CaseSensitivity cs = QuaZip::convertCaseSensitivity(
0484                     d->caseSensitivity);
0485             if (filePath.endsWith('/')) {
0486                 return entries.contains(filePath, cs);
0487             } else {
0488                 return entries.contains(fileName, cs)
0489                     || entries.contains(fileName + "/", cs);
0490             }
0491         }
0492     }
0493 }
0494 
0495 bool QuaZipDir::exists() const
0496 {
0497     return QuaZipDir(d->zip).exists(d->dir);
0498 }
0499 
0500 QString QuaZipDir::filePath(const QString &fileName) const
0501 {
0502     return QDir(d->dir).filePath(fileName);
0503 }
0504 
0505 QDir::Filters QuaZipDir::filter()
0506 {
0507     return d->filter;
0508 }
0509 
0510 bool QuaZipDir::isRoot() const
0511 {
0512     return d->simplePath().isEmpty();
0513 }
0514 
0515 QStringList QuaZipDir::nameFilters() const
0516 {
0517     return d->nameFilters;
0518 }
0519 
0520 QString QuaZipDir::path() const
0521 {
0522     return d->dir;
0523 }
0524 
0525 QString QuaZipDir::relativeFilePath(const QString &fileName) const
0526 {
0527     return QDir("/" + d->dir).relativeFilePath(fileName);
0528 }
0529 
0530 void QuaZipDir::setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity)
0531 {
0532     d->caseSensitivity = caseSensitivity;
0533 }
0534 
0535 void QuaZipDir::setFilter(QDir::Filters filters)
0536 {
0537     d->filter = filters;
0538 }
0539 
0540 void QuaZipDir::setNameFilters(const QStringList &nameFilters)
0541 {
0542     d->nameFilters = nameFilters;
0543 }
0544 
0545 void QuaZipDir::setPath(const QString &path)
0546 {
0547     QString newDir = path;
0548     if (newDir == "/") {
0549         d->dir = "";
0550     } else {
0551         if (newDir.endsWith('/'))
0552             newDir.chop(1);
0553         if (newDir.startsWith('/'))
0554             newDir = newDir.mid(1);
0555         d->dir = newDir;
0556     }
0557 }
0558 
0559 void QuaZipDir::setSorting(QDir::SortFlags sort)
0560 {
0561     d->sorting = sort;
0562 }
0563 
0564 QDir::SortFlags QuaZipDir::sorting() const
0565 {
0566     return d->sorting;
0567 }