source: Vago/Libs/quazip-0.7.2/quazip/quazipfile.cpp@ 1088

Last change on this file since 1088 was 1050, checked in by s10k, 8 years ago
File size: 14.9 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, see
22quazip/(un)zip.h files for details, basically it's zlib license.
23 **/
24
25#include "quazipfile.h"
26
27using namespace std;
28
29/// The implementation class for QuaZip.
30/**
31\internal
32
33This class contains all the private stuff for the QuaZipFile class, thus
34allowing to preserve binary compatibility between releases, the
35technique known as the Pimpl (private implementation) idiom.
36*/
37class QuaZipFilePrivate {
38 friend class QuaZipFile;
39 private:
40 Q_DISABLE_COPY(QuaZipFilePrivate)
41 /// The pointer to the associated QuaZipFile instance.
42 QuaZipFile *q;
43 /// The QuaZip object to work with.
44 QuaZip *zip;
45 /// The file name.
46 QString fileName;
47 /// Case sensitivity mode.
48 QuaZip::CaseSensitivity caseSensitivity;
49 /// Whether this file is opened in the raw mode.
50 bool raw;
51 /// Write position to keep track of.
52 /**
53 QIODevice::pos() is broken for non-seekable devices, so we need
54 our own position.
55 */
56 qint64 writePos;
57 /// Uncompressed size to write along with a raw file.
58 quint64 uncompressedSize;
59 /// CRC to write along with a raw file.
60 quint32 crc;
61 /// Whether \ref zip points to an internal QuaZip instance.
62 /**
63 This is true if the archive was opened by name, rather than by
64 supplying an existing QuaZip instance.
65 */
66 bool internal;
67 /// The last error.
68 int zipError;
69 /// Resets \ref zipError.
70 inline void resetZipError() const {setZipError(UNZ_OK);}
71 /// Sets the zip error.
72 /**
73 This function is marked as const although it changes one field.
74 This allows to call it from const functions that don't change
75 anything by themselves.
76 */
77 void setZipError(int zipError) const;
78 /// The constructor for the corresponding QuaZipFile constructor.
79 inline QuaZipFilePrivate(QuaZipFile *q):
80 q(q),
81 zip(NULL),
82 caseSensitivity(QuaZip::csDefault),
83 raw(false),
84 writePos(0),
85 uncompressedSize(0),
86 crc(0),
87 internal(true),
88 zipError(UNZ_OK) {}
89 /// The constructor for the corresponding QuaZipFile constructor.
90 inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName):
91 q(q),
92 caseSensitivity(QuaZip::csDefault),
93 raw(false),
94 writePos(0),
95 uncompressedSize(0),
96 crc(0),
97 internal(true),
98 zipError(UNZ_OK)
99 {
100 zip=new QuaZip(zipName);
101 }
102 /// The constructor for the corresponding QuaZipFile constructor.
103 inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName,
104 QuaZip::CaseSensitivity cs):
105 q(q),
106 raw(false),
107 writePos(0),
108 uncompressedSize(0),
109 crc(0),
110 internal(true),
111 zipError(UNZ_OK)
112 {
113 zip=new QuaZip(zipName);
114 this->fileName=fileName;
115 if (this->fileName.startsWith('/'))
116 this->fileName = this->fileName.mid(1);
117 this->caseSensitivity=cs;
118 }
119 /// The constructor for the QuaZipFile constructor accepting a file name.
120 inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip):
121 q(q),
122 zip(zip),
123 raw(false),
124 writePos(0),
125 uncompressedSize(0),
126 crc(0),
127 internal(false),
128 zipError(UNZ_OK) {}
129 /// The destructor.
130 inline ~QuaZipFilePrivate()
131 {
132 if (internal)
133 delete zip;
134 }
135};
136
137QuaZipFile::QuaZipFile():
138 p(new QuaZipFilePrivate(this))
139{
140}
141
142QuaZipFile::QuaZipFile(QObject *parent):
143 QIODevice(parent),
144 p(new QuaZipFilePrivate(this))
145{
146}
147
148QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent):
149 QIODevice(parent),
150 p(new QuaZipFilePrivate(this, zipName))
151{
152}
153
154QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName,
155 QuaZip::CaseSensitivity cs, QObject *parent):
156 QIODevice(parent),
157 p(new QuaZipFilePrivate(this, zipName, fileName, cs))
158{
159}
160
161QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent):
162 QIODevice(parent),
163 p(new QuaZipFilePrivate(this, zip))
164{
165}
166
167QuaZipFile::~QuaZipFile()
168{
169 if (isOpen())
170 close();
171 delete p;
172}
173
174QString QuaZipFile::getZipName() const
175{
176 return p->zip==NULL ? QString() : p->zip->getZipName();
177}
178
179QuaZip *QuaZipFile::getZip() const
180{
181 return p->internal ? NULL : p->zip;
182}
183
184QString QuaZipFile::getActualFileName()const
185{
186 p->setZipError(UNZ_OK);
187 if (p->zip == NULL || (openMode() & WriteOnly))
188 return QString();
189 QString name=p->zip->getCurrentFileName();
190 if(name.isNull())
191 p->setZipError(p->zip->getZipError());
192 return name;
193}
194
195void QuaZipFile::setZipName(const QString& zipName)
196{
197 if(isOpen()) {
198 qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name");
199 return;
200 }
201 if(p->zip!=NULL && p->internal)
202 delete p->zip;
203 p->zip=new QuaZip(zipName);
204 p->internal=true;
205}
206
207void QuaZipFile::setZip(QuaZip *zip)
208{
209 if(isOpen()) {
210 qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP");
211 return;
212 }
213 if(p->zip!=NULL && p->internal)
214 delete p->zip;
215 p->zip=zip;
216 p->fileName=QString();
217 p->internal=false;
218}
219
220void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs)
221{
222 if(p->zip==NULL) {
223 qWarning("QuaZipFile::setFileName(): call setZipName() first");
224 return;
225 }
226 if(!p->internal) {
227 qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip");
228 return;
229 }
230 if(isOpen()) {
231 qWarning("QuaZipFile::setFileName(): can not set file name for already opened file");
232 return;
233 }
234 p->fileName=fileName;
235 if (p->fileName.startsWith('/'))
236 p->fileName = p->fileName.mid(1);
237 p->caseSensitivity=cs;
238}
239
240void QuaZipFilePrivate::setZipError(int zipError) const
241{
242 QuaZipFilePrivate *fakeThis = const_cast<QuaZipFilePrivate*>(this); // non-const
243 fakeThis->zipError=zipError;
244 if(zipError==UNZ_OK)
245 q->setErrorString(QString());
246 else
247 q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError));
248}
249
250bool QuaZipFile::open(OpenMode mode)
251{
252 return open(mode, NULL);
253}
254
255bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password)
256{
257 p->resetZipError();
258 if(isOpen()) {
259 qWarning("QuaZipFile::open(): already opened");
260 return false;
261 }
262 if(mode&Unbuffered) {
263 qWarning("QuaZipFile::open(): Unbuffered mode is not supported");
264 return false;
265 }
266 if((mode&ReadOnly)&&!(mode&WriteOnly)) {
267 if(p->internal) {
268 if(!p->zip->open(QuaZip::mdUnzip)) {
269 p->setZipError(p->zip->getZipError());
270 return false;
271 }
272 if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) {
273 p->setZipError(p->zip->getZipError());
274 p->zip->close();
275 return false;
276 }
277 } else {
278 if(p->zip==NULL) {
279 qWarning("QuaZipFile::open(): zip is NULL");
280 return false;
281 }
282 if(p->zip->getMode()!=QuaZip::mdUnzip) {
283 qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
284 (int)mode, (int)p->zip->getMode());
285 return false;
286 }
287 if(!p->zip->hasCurrentFile()) {
288 qWarning("QuaZipFile::open(): zip does not have current file");
289 return false;
290 }
291 }
292 p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password));
293 if(p->zipError==UNZ_OK) {
294 setOpenMode(mode);
295 p->raw=raw;
296 return true;
297 } else
298 return false;
299 }
300 qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
301 return false;
302}
303
304bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info,
305 const char *password, quint32 crc,
306 int method, int level, bool raw,
307 int windowBits, int memLevel, int strategy)
308{
309 zip_fileinfo info_z;
310 p->resetZipError();
311 if(isOpen()) {
312 qWarning("QuaZipFile::open(): already opened");
313 return false;
314 }
315 if((mode&WriteOnly)&&!(mode&ReadOnly)) {
316 if(p->internal) {
317 qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach");
318 return false;
319 }
320 if(p->zip==NULL) {
321 qWarning("QuaZipFile::open(): zip is NULL");
322 return false;
323 }
324 if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) {
325 qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d",
326 (int)mode, (int)p->zip->getMode());
327 return false;
328 }
329 info_z.tmz_date.tm_year=info.dateTime.date().year();
330 info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1;
331 info_z.tmz_date.tm_mday=info.dateTime.date().day();
332 info_z.tmz_date.tm_hour=info.dateTime.time().hour();
333 info_z.tmz_date.tm_min=info.dateTime.time().minute();
334 info_z.tmz_date.tm_sec=info.dateTime.time().second();
335 info_z.dosDate = 0;
336 info_z.internal_fa=(uLong)info.internalAttr;
337 info_z.external_fa=(uLong)info.externalAttr;
338 if (p->zip->isDataDescriptorWritingEnabled())
339 zipSetFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR);
340 else
341 zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR);
342 p->setZipError(zipOpenNewFileInZip3_64(p->zip->getZipFile(),
343 p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z,
344 info.extraLocal.constData(), info.extraLocal.length(),
345 info.extraGlobal.constData(), info.extraGlobal.length(),
346 p->zip->getCommentCodec()->fromUnicode(info.comment).constData(),
347 method, level, (int)raw,
348 windowBits, memLevel, strategy,
349 password, (uLong)crc, p->zip->isZip64Enabled()));
350 if(p->zipError==UNZ_OK) {
351 p->writePos=0;
352 setOpenMode(mode);
353 p->raw=raw;
354 if(raw) {
355 p->crc=crc;
356 p->uncompressedSize=info.uncompressedSize;
357 }
358 return true;
359 } else
360 return false;
361 }
362 qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode);
363 return false;
364}
365
366bool QuaZipFile::isSequential()const
367{
368 return true;
369}
370
371qint64 QuaZipFile::pos()const
372{
373 if(p->zip==NULL) {
374 qWarning("QuaZipFile::pos(): call setZipName() or setZip() first");
375 return -1;
376 }
377 if(!isOpen()) {
378 qWarning("QuaZipFile::pos(): file is not open");
379 return -1;
380 }
381 if(openMode()&ReadOnly)
382 // QIODevice::pos() is broken for sequential devices,
383 // but thankfully bytesAvailable() returns the number of
384 // bytes buffered, so we know how far ahead we are.
385 return unztell(p->zip->getUnzFile()) - QIODevice::bytesAvailable();
386 else
387 return p->writePos;
388}
389
390bool QuaZipFile::atEnd()const
391{
392 if(p->zip==NULL) {
393 qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first");
394 return false;
395 }
396 if(!isOpen()) {
397 qWarning("QuaZipFile::atEnd(): file is not open");
398 return false;
399 }
400 if(openMode()&ReadOnly)
401 // the same problem as with pos()
402 return QIODevice::bytesAvailable() == 0
403 && unzeof(p->zip->getUnzFile())==1;
404 else
405 return true;
406}
407
408qint64 QuaZipFile::size()const
409{
410 if(!isOpen()) {
411 qWarning("QuaZipFile::atEnd(): file is not open");
412 return -1;
413 }
414 if(openMode()&ReadOnly)
415 return p->raw?csize():usize();
416 else
417 return p->writePos;
418}
419
420qint64 QuaZipFile::csize()const
421{
422 unz_file_info64 info_z;
423 p->setZipError(UNZ_OK);
424 if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
425 p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
426 if(p->zipError!=UNZ_OK)
427 return -1;
428 return info_z.compressed_size;
429}
430
431qint64 QuaZipFile::usize()const
432{
433 unz_file_info64 info_z;
434 p->setZipError(UNZ_OK);
435 if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1;
436 p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0));
437 if(p->zipError!=UNZ_OK)
438 return -1;
439 return info_z.uncompressed_size;
440}
441
442bool QuaZipFile::getFileInfo(QuaZipFileInfo *info)
443{
444 QuaZipFileInfo64 info64;
445 if (getFileInfo(&info64)) {
446 info64.toQuaZipFileInfo(*info);
447 return true;
448 } else {
449 return false;
450 }
451}
452
453bool QuaZipFile::getFileInfo(QuaZipFileInfo64 *info)
454{
455 if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false;
456 p->zip->getCurrentFileInfo(info);
457 p->setZipError(p->zip->getZipError());
458 return p->zipError==UNZ_OK;
459}
460
461void QuaZipFile::close()
462{
463 p->resetZipError();
464 if(p->zip==NULL||!p->zip->isOpen()) return;
465 if(!isOpen()) {
466 qWarning("QuaZipFile::close(): file isn't open");
467 return;
468 }
469 if(openMode()&ReadOnly)
470 p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile()));
471 else if(openMode()&WriteOnly)
472 if(isRaw()) p->setZipError(zipCloseFileInZipRaw64(p->zip->getZipFile(), p->uncompressedSize, p->crc));
473 else p->setZipError(zipCloseFileInZip(p->zip->getZipFile()));
474 else {
475 qWarning("Wrong open mode: %d", (int)openMode());
476 return;
477 }
478 if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen);
479 else return;
480 if(p->internal) {
481 p->zip->close();
482 p->setZipError(p->zip->getZipError());
483 }
484}
485
486qint64 QuaZipFile::readData(char *data, qint64 maxSize)
487{
488 p->setZipError(UNZ_OK);
489 qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize);
490 if (bytesRead < 0) {
491 p->setZipError((int) bytesRead);
492 return -1;
493 }
494 return bytesRead;
495}
496
497qint64 QuaZipFile::writeData(const char* data, qint64 maxSize)
498{
499 p->setZipError(ZIP_OK);
500 p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize));
501 if(p->zipError!=ZIP_OK) return -1;
502 else {
503 p->writePos+=maxSize;
504 return maxSize;
505 }
506}
507
508QString QuaZipFile::getFileName() const
509{
510 return p->fileName;
511}
512
513QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const
514{
515 return p->caseSensitivity;
516}
517
518bool QuaZipFile::isRaw() const
519{
520 return p->raw;
521}
522
523int QuaZipFile::getZipError() const
524{
525 return p->zipError;
526}
527
528qint64 QuaZipFile::bytesAvailable() const
529{
530 return size() - pos();
531}
Note: See TracBrowser for help on using the repository browser.