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 }