source: Vago/quazip-0.7.2/quazip/quazipdir.cpp@ 1091

Last change on this file since 1091 was 1049, checked in by s10k, 8 years ago
File size: 16.5 KB
Line 
1/*
2Copyright (C) 2005-2014 Sergey A. Tachenov
3
4This file is part of QuaZIP.
5
6QuaZIP is free software: you can redistribute it and/or modify
7it under the terms of the GNU Lesser General Public License as published by
8the Free Software Foundation, either version 2.1 of the License, or
9(at your option) any later version.
10
11QuaZIP is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU Lesser General Public License for more details.
15
16You should have received a copy of the GNU Lesser General Public License
17along with QuaZIP. If not, see <http://www.gnu.org/licenses/>.
18
19See COPYING file for the full LGPL text.
20
21Original ZIP package is copyrighted by Gilles Vollant and contributors,
22see quazip/(un)zip.h files for details. Basically it's the zlib license.
23*/
24
25#include "quazipdir.h"
26
27#include <QSet>
28#include <QSharedData>
29
30/// \cond internal
31class QuaZipDirPrivate: public QSharedData {
32 friend class QuaZipDir;
33private:
34 QuaZipDirPrivate(QuaZip *zip, const QString &dir = QString()):
35 zip(zip), dir(dir), caseSensitivity(QuaZip::csDefault),
36 filter(QDir::NoFilter), sorting(QDir::NoSort) {}
37 QuaZip *zip;
38 QString dir;
39 QuaZip::CaseSensitivity caseSensitivity;
40 QDir::Filters filter;
41 QStringList nameFilters;
42 QDir::SortFlags sorting;
43 template<typename TFileInfoList>
44 bool entryInfoList(QStringList nameFilters, QDir::Filters filter,
45 QDir::SortFlags sort, TFileInfoList &result) const;
46 inline QString simplePath() const {return QDir::cleanPath(dir);}
47};
48/// \endcond
49
50QuaZipDir::QuaZipDir(const QuaZipDir &that):
51 d(that.d)
52{
53}
54
55QuaZipDir::QuaZipDir(QuaZip *zip, const QString &dir):
56 d(new QuaZipDirPrivate(zip, dir))
57{
58 if (d->dir.startsWith('/'))
59 d->dir = d->dir.mid(1);
60}
61
62QuaZipDir::~QuaZipDir()
63{
64}
65
66bool QuaZipDir::operator==(const QuaZipDir &that)
67{
68 return d->zip == that.d->zip && d->dir == that.d->dir;
69}
70
71QuaZipDir& QuaZipDir::operator=(const QuaZipDir &that)
72{
73 this->d = that.d;
74 return *this;
75}
76
77QString QuaZipDir::operator[](int pos) const
78{
79 return entryList().at(pos);
80}
81
82QuaZip::CaseSensitivity QuaZipDir::caseSensitivity() const
83{
84 return d->caseSensitivity;
85}
86
87bool QuaZipDir::cd(const QString &directoryName)
88{
89 if (directoryName == "/") {
90 d->dir = "";
91 return true;
92 }
93 QString dirName = directoryName;
94 if (dirName.endsWith('/'))
95 dirName.chop(1);
96 if (dirName.contains('/')) {
97 QuaZipDir dir(*this);
98 if (dirName.startsWith('/')) {
99#ifdef QUAZIP_QUAZIPDIR_DEBUG
100 qDebug("QuaZipDir::cd(%s): going to /",
101 dirName.toUtf8().constData());
102#endif
103 if (!dir.cd("/"))
104 return false;
105 }
106 QStringList path = dirName.split('/', QString::SkipEmptyParts);
107 for (QStringList::const_iterator i = path.constBegin();
108 i != path.end();
109 ++i) {
110 const QString &step = *i;
111#ifdef QUAZIP_QUAZIPDIR_DEBUG
112 qDebug("QuaZipDir::cd(%s): going to %s",
113 dirName.toUtf8().constData(),
114 step.toUtf8().constData());
115#endif
116 if (!dir.cd(step))
117 return false;
118 }
119 d->dir = dir.path();
120 return true;
121 } else { // no '/'
122 if (dirName == ".") {
123 return true;
124 } else if (dirName == "..") {
125 if (isRoot()) {
126 return false;
127 } else {
128 int slashPos = d->dir.lastIndexOf('/');
129 if (slashPos == -1) {
130 d->dir = "";
131 } else {
132 d->dir = d->dir.left(slashPos);
133 }
134 return true;
135 }
136 } else { // a simple subdirectory
137 if (exists(dirName)) {
138 if (isRoot())
139 d->dir = dirName;
140 else
141 d->dir += "/" + dirName;
142 return true;
143 } else {
144 return false;
145 }
146 }
147 }
148}
149
150bool QuaZipDir::cdUp()
151{
152 return cd("..");
153}
154
155uint QuaZipDir::count() const
156{
157 return entryList().count();
158}
159
160QString QuaZipDir::dirName() const
161{
162 return QDir(d->dir).dirName();
163}
164
165QuaZipFileInfo64 QuaZipDir_getFileInfo(QuaZip *zip, bool *ok,
166 const QString &relativeName,
167 bool isReal)
168{
169 QuaZipFileInfo64 info;
170 if (isReal) {
171 *ok = zip->getCurrentFileInfo(&info);
172 } else {
173 *ok = true;
174 info.compressedSize = 0;
175 info.crc = 0;
176 info.diskNumberStart = 0;
177 info.externalAttr = 0;
178 info.flags = 0;
179 info.internalAttr = 0;
180 info.method = 0;
181 info.uncompressedSize = 0;
182 info.versionCreated = info.versionNeeded = 0;
183 }
184 info.name = relativeName;
185 return info;
186}
187
188static void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo64> &from,
189 QList<QuaZipFileInfo64> &to)
190{
191 to = from;
192}
193
194static void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo64> &from,
195 QStringList &to)
196{
197 to.clear();
198 for (QList<QuaZipFileInfo64>::const_iterator i = from.constBegin();
199 i != from.constEnd();
200 ++i) {
201 to.append(i->name);
202 }
203}
204
205static void QuaZipDir_convertInfoList(const QList<QuaZipFileInfo64> &from,
206 QList<QuaZipFileInfo> &to)
207{
208 to.clear();
209 for (QList<QuaZipFileInfo64>::const_iterator i = from.constBegin();
210 i != from.constEnd();
211 ++i) {
212 QuaZipFileInfo info32;
213 i->toQuaZipFileInfo(info32);
214 to.append(info32);
215 }
216}
217
218/// \cond internal
219/**
220 An utility class to restore the current file.
221 */
222class QuaZipDirRestoreCurrent {
223public:
224 inline QuaZipDirRestoreCurrent(QuaZip *zip):
225 zip(zip), currentFile(zip->getCurrentFileName()) {}
226 inline ~QuaZipDirRestoreCurrent()
227 {
228 zip->setCurrentFile(currentFile);
229 }
230private:
231 QuaZip *zip;
232 QString currentFile;
233};
234/// \endcond
235
236/// \cond internal
237class QuaZipDirComparator
238{
239 private:
240 QDir::SortFlags sort;
241 static QString getExtension(const QString &name);
242 int compareStrings(const QString &string1, const QString &string2);
243 public:
244 inline QuaZipDirComparator(QDir::SortFlags sort): sort(sort) {}
245 bool operator()(const QuaZipFileInfo64 &info1, const QuaZipFileInfo64 &info2);
246};
247
248QString QuaZipDirComparator::getExtension(const QString &name)
249{
250 if (name.endsWith('.') || name.indexOf('.', 1) == -1) {
251 return "";
252 } else {
253 return name.mid(name.lastIndexOf('.') + 1);
254 }
255
256}
257
258int QuaZipDirComparator::compareStrings(const QString &string1,
259 const QString &string2)
260{
261 if (sort & QDir::LocaleAware) {
262 if (sort & QDir::IgnoreCase) {
263 return string1.toLower().localeAwareCompare(string2.toLower());
264 } else {
265 return string1.localeAwareCompare(string2);
266 }
267 } else {
268 return string1.compare(string2, (sort & QDir::IgnoreCase)
269 ? Qt::CaseInsensitive : Qt::CaseSensitive);
270 }
271}
272
273bool QuaZipDirComparator::operator()(const QuaZipFileInfo64 &info1,
274 const QuaZipFileInfo64 &info2)
275{
276 QDir::SortFlags order = sort
277 & (QDir::Name | QDir::Time | QDir::Size | QDir::Type);
278 if ((sort & QDir::DirsFirst) == QDir::DirsFirst
279 || (sort & QDir::DirsLast) == QDir::DirsLast) {
280 if (info1.name.endsWith('/') && !info2.name.endsWith('/'))
281 return (sort & QDir::DirsFirst) == QDir::DirsFirst;
282 else if (!info1.name.endsWith('/') && info2.name.endsWith('/'))
283 return (sort & QDir::DirsLast) == QDir::DirsLast;
284 }
285 bool result;
286 int extDiff;
287 switch (order) {
288 case QDir::Name:
289 result = compareStrings(info1.name, info2.name) < 0;
290 break;
291 case QDir::Type:
292 extDiff = compareStrings(getExtension(info1.name),
293 getExtension(info2.name));
294 if (extDiff == 0) {
295 result = compareStrings(info1.name, info2.name) < 0;
296 } else {
297 result = extDiff < 0;
298 }
299 break;
300 case QDir::Size:
301 if (info1.uncompressedSize == info2.uncompressedSize) {
302 result = compareStrings(info1.name, info2.name) < 0;
303 } else {
304 result = info1.uncompressedSize < info2.uncompressedSize;
305 }
306 break;
307 case QDir::Time:
308 if (info1.dateTime == info2.dateTime) {
309 result = compareStrings(info1.name, info2.name) < 0;
310 } else {
311 result = info1.dateTime < info2.dateTime;
312 }
313 break;
314 default:
315 qWarning("QuaZipDirComparator(): Invalid sort mode 0x%2X",
316 static_cast<unsigned>(sort));
317 return false;
318 }
319 return (sort & QDir::Reversed) ? !result : result;
320}
321
322template<typename TFileInfoList>
323bool QuaZipDirPrivate::entryInfoList(QStringList nameFilters,
324 QDir::Filters filter, QDir::SortFlags sort, TFileInfoList &result) const
325{
326 QString basePath = simplePath();
327 if (!basePath.isEmpty())
328 basePath += "/";
329 int baseLength = basePath.length();
330 result.clear();
331 QuaZipDirRestoreCurrent saveCurrent(zip);
332 if (!zip->goToFirstFile()) {
333 return zip->getZipError() == UNZ_OK;
334 }
335 QDir::Filters fltr = filter;
336 if (fltr == QDir::NoFilter)
337 fltr = this->filter;
338 if (fltr == QDir::NoFilter)
339 fltr = QDir::AllEntries;
340 QStringList nmfltr = nameFilters;
341 if (nmfltr.isEmpty())
342 nmfltr = this->nameFilters;
343 QSet<QString> dirsFound;
344 QList<QuaZipFileInfo64> list;
345 do {
346 QString name = zip->getCurrentFileName();
347 if (!name.startsWith(basePath))
348 continue;
349 QString relativeName = name.mid(baseLength);
350 if (relativeName.isEmpty())
351 continue;
352 bool isDir = false;
353 bool isReal = true;
354 if (relativeName.contains('/')) {
355 int indexOfSlash = relativeName.indexOf('/');
356 // something like "subdir/"
357 isReal = indexOfSlash == relativeName.length() - 1;
358 relativeName = relativeName.left(indexOfSlash + 1);
359 if (dirsFound.contains(relativeName))
360 continue;
361 isDir = true;
362 }
363 dirsFound.insert(relativeName);
364 if ((fltr & QDir::Dirs) == 0 && isDir)
365 continue;
366 if ((fltr & QDir::Files) == 0 && !isDir)
367 continue;
368 if (!nmfltr.isEmpty() && !QDir::match(nmfltr, relativeName))
369 continue;
370 bool ok;
371 QuaZipFileInfo64 info = QuaZipDir_getFileInfo(zip, &ok, relativeName,
372 isReal);
373 if (!ok) {
374 return false;
375 }
376 list.append(info);
377 } while (zip->goToNextFile());
378 QDir::SortFlags srt = sort;
379 if (srt == QDir::NoSort)
380 srt = sorting;
381#ifdef QUAZIP_QUAZIPDIR_DEBUG
382 qDebug("QuaZipDirPrivate::entryInfoList(): before sort:");
383 foreach (QuaZipFileInfo64 info, list) {
384 qDebug("%s\t%s", info.name.toUtf8().constData(),
385 info.dateTime.toString(Qt::ISODate).toUtf8().constData());
386 }
387#endif
388 if (srt != QDir::NoSort && (srt & QDir::Unsorted) != QDir::Unsorted) {
389 if (QuaZip::convertCaseSensitivity(caseSensitivity)
390 == Qt::CaseInsensitive)
391 srt |= QDir::IgnoreCase;
392 QuaZipDirComparator lessThan(srt);
393 qSort(list.begin(), list.end(), lessThan);
394 }
395 QuaZipDir_convertInfoList(list, result);
396 return true;
397}
398
399/// \endcond
400
401QList<QuaZipFileInfo> QuaZipDir::entryInfoList(const QStringList &nameFilters,
402 QDir::Filters filters, QDir::SortFlags sort) const
403{
404 QList<QuaZipFileInfo> result;
405 if (d->entryInfoList(nameFilters, filters, sort, result))
406 return result;
407 else
408 return QList<QuaZipFileInfo>();
409}
410
411QList<QuaZipFileInfo> QuaZipDir::entryInfoList(QDir::Filters filters,
412 QDir::SortFlags sort) const
413{
414 return entryInfoList(QStringList(), filters, sort);
415}
416
417QList<QuaZipFileInfo64> QuaZipDir::entryInfoList64(const QStringList &nameFilters,
418 QDir::Filters filters, QDir::SortFlags sort) const
419{
420 QList<QuaZipFileInfo64> result;
421 if (d->entryInfoList(nameFilters, filters, sort, result))
422 return result;
423 else
424 return QList<QuaZipFileInfo64>();
425}
426
427QList<QuaZipFileInfo64> QuaZipDir::entryInfoList64(QDir::Filters filters,
428 QDir::SortFlags sort) const
429{
430 return entryInfoList64(QStringList(), filters, sort);
431}
432
433QStringList QuaZipDir::entryList(const QStringList &nameFilters,
434 QDir::Filters filters, QDir::SortFlags sort) const
435{
436 QStringList result;
437 if (d->entryInfoList(nameFilters, filters, sort, result))
438 return result;
439 else
440 return QStringList();
441}
442
443QStringList QuaZipDir::entryList(QDir::Filters filters,
444 QDir::SortFlags sort) const
445{
446 return entryList(QStringList(), filters, sort);
447}
448
449bool QuaZipDir::exists(const QString &filePath) const
450{
451 if (filePath == "/" || filePath.isEmpty())
452 return true;
453 QString fileName = filePath;
454 if (fileName.endsWith('/'))
455 fileName.chop(1);
456 if (fileName.contains('/')) {
457 QFileInfo fileInfo(fileName);
458#ifdef QUAZIP_QUAZIPDIR_DEBUG
459 qDebug("QuaZipDir::exists(): fileName=%s, fileInfo.fileName()=%s, "
460 "fileInfo.path()=%s", fileName.toUtf8().constData(),
461 fileInfo.fileName().toUtf8().constData(),
462 fileInfo.path().toUtf8().constData());
463#endif
464 QuaZipDir dir(*this);
465 return dir.cd(fileInfo.path()) && dir.exists(fileInfo.fileName());
466 } else {
467 if (fileName == "..") {
468 return !isRoot();
469 } else if (fileName == ".") {
470 return true;
471 } else {
472 QStringList entries = entryList(QDir::AllEntries, QDir::NoSort);
473#ifdef QUAZIP_QUAZIPDIR_DEBUG
474 qDebug("QuaZipDir::exists(): looking for %s",
475 fileName.toUtf8().constData());
476 for (QStringList::const_iterator i = entries.constBegin();
477 i != entries.constEnd();
478 ++i) {
479 qDebug("QuaZipDir::exists(): entry: %s",
480 i->toUtf8().constData());
481 }
482#endif
483 Qt::CaseSensitivity cs = QuaZip::convertCaseSensitivity(
484 d->caseSensitivity);
485 if (filePath.endsWith('/')) {
486 return entries.contains(filePath, cs);
487 } else {
488 return entries.contains(fileName, cs)
489 || entries.contains(fileName + "/", cs);
490 }
491 }
492 }
493}
494
495bool QuaZipDir::exists() const
496{
497 return QuaZipDir(d->zip).exists(d->dir);
498}
499
500QString QuaZipDir::filePath(const QString &fileName) const
501{
502 return QDir(d->dir).filePath(fileName);
503}
504
505QDir::Filters QuaZipDir::filter()
506{
507 return d->filter;
508}
509
510bool QuaZipDir::isRoot() const
511{
512 return d->simplePath().isEmpty();
513}
514
515QStringList QuaZipDir::nameFilters() const
516{
517 return d->nameFilters;
518}
519
520QString QuaZipDir::path() const
521{
522 return d->dir;
523}
524
525QString QuaZipDir::relativeFilePath(const QString &fileName) const
526{
527 return QDir("/" + d->dir).relativeFilePath(fileName);
528}
529
530void QuaZipDir::setCaseSensitivity(QuaZip::CaseSensitivity caseSensitivity)
531{
532 d->caseSensitivity = caseSensitivity;
533}
534
535void QuaZipDir::setFilter(QDir::Filters filters)
536{
537 d->filter = filters;
538}
539
540void QuaZipDir::setNameFilters(const QStringList &nameFilters)
541{
542 d->nameFilters = nameFilters;
543}
544
545void QuaZipDir::setPath(const QString &path)
546{
547 QString newDir = path;
548 if (newDir == "/") {
549 d->dir = "";
550 } else {
551 if (newDir.endsWith('/'))
552 newDir.chop(1);
553 if (newDir.startsWith('/'))
554 newDir = newDir.mid(1);
555 d->dir = newDir;
556 }
557}
558
559void QuaZipDir::setSorting(QDir::SortFlags sort)
560{
561 d->sorting = sort;
562}
563
564QDir::SortFlags QuaZipDir::sorting() const
565{
566 return d->sorting;
567}
Note: See TracBrowser for help on using the repository browser.