1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 #ifndef QT_NO_TEXTODFWRITER
46 #include "qzipreader_p.h"
47 #include "qzipwriter_p.h"
48 #include <qdatetime.h>
49 #include <qplatformdefs.h>
58 # define S_IFREG 0100000
60 # define S_IFDIR 0040000
63 # define S_ISDIR(x) ((x) & S_IFDIR) > 0
66 # define S_ISREG(x) ((x) & 0170000) == S_IFREG
68 # define S_IFLNK 020000
69 # define S_ISLNK(x) ((x) & S_IFLNK) > 0
90 #define ZDEBUG if (0) qDebug
95 static inline uint readUInt(const uchar *data)
97 return (data[0]) + (data[1]<<8) + (data[2]<<16) + (data[3]<<24);
100 static inline ushort readUShort(const uchar *data)
102 return (data[0]) + (data[1]<<8);
105 static inline void writeUInt(uchar *data, uint i)
108 data[1] = (i>>8) & 0xff;
109 data[2] = (i>>16) & 0xff;
110 data[3] = (i>>24) & 0xff;
113 static inline void writeUShort(uchar *data, ushort i)
116 data[1] = (i>>8) & 0xff;
119 static inline void copyUInt(uchar *dest, const uchar *src)
127 static inline void copyUShort(uchar *dest, const uchar *src)
133 static void writeMSDosDate(uchar *dest, const QDateTime& dt)
137 (dt.time().hour() << 11) // 5 bit hour
138 | (dt.time().minute() << 5) // 6 bit minute
139 | (dt.time().second() >> 1); // 5 bit double seconds
141 dest[0] = time & 0xff;
145 ((dt.date().year() - 1980) << 9) // 7 bit year 1980-based
146 | (dt.date().month() << 5) // 4 bit month
147 | (dt.date().day()); // 5 bit day
149 dest[2] = char(date);
150 dest[3] = char(date >> 8);
159 static quint32 permissionsToMode(QFile::Permissions perms)
162 if (perms & QFile::ReadOwner)
164 if (perms & QFile::WriteOwner)
166 if (perms & QFile::ExeOwner)
168 if (perms & QFile::ReadUser)
170 if (perms & QFile::WriteUser)
172 if (perms & QFile::ExeUser)
174 if (perms & QFile::ReadGroup)
176 if (perms & QFile::WriteGroup)
178 if (perms & QFile::ExeGroup)
180 if (perms & QFile::ReadOther)
182 if (perms & QFile::WriteOther)
184 if (perms & QFile::ExeOther)
189 static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
194 stream.next_in = (Bytef*)source;
195 stream.avail_in = (uInt)sourceLen;
196 if ((uLong)stream.avail_in != sourceLen)
199 stream.next_out = dest;
200 stream.avail_out = (uInt)*destLen;
201 if ((uLong)stream.avail_out != *destLen)
204 stream.zalloc = (alloc_func)0;
205 stream.zfree = (free_func)0;
207 err = inflateInit2(&stream, -MAX_WBITS);
211 err = inflate(&stream, Z_FINISH);
212 if (err != Z_STREAM_END) {
214 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
218 *destLen = stream.total_out;
220 err = inflateEnd(&stream);
224 static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
229 stream.next_in = (Bytef*)source;
230 stream.avail_in = (uInt)sourceLen;
231 stream.next_out = dest;
232 stream.avail_out = (uInt)*destLen;
233 if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
235 stream.zalloc = (alloc_func)0;
236 stream.zfree = (free_func)0;
237 stream.opaque = (voidpf)0;
239 err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
240 if (err != Z_OK) return err;
242 err = deflate(&stream, Z_FINISH);
243 if (err != Z_STREAM_END) {
245 return err == Z_OK ? Z_BUF_ERROR : err;
247 *destLen = stream.total_out;
249 err = deflateEnd(&stream);
253 static QFile::Permissions modeToPermissions(quint32 mode)
255 QFile::Permissions ret;
257 ret |= QFile::ReadOwner;
259 ret |= QFile::WriteOwner;
261 ret |= QFile::ExeOwner;
263 ret |= QFile::ReadUser;
265 ret |= QFile::WriteUser;
267 ret |= QFile::ExeUser;
269 ret |= QFile::ReadGroup;
271 ret |= QFile::WriteGroup;
273 ret |= QFile::ExeGroup;
275 ret |= QFile::ReadOther;
277 ret |= QFile::WriteOther;
279 ret |= QFile::ExeOther;
283 static QDateTime readMSDosDate(const uchar *src)
285 uint dosDate = readUInt(src);
287 uDate = (quint64)(dosDate >> 16);
288 uint tm_mday = (uDate & 0x1f);
289 uint tm_mon = ((uDate & 0x1E0) >> 5);
290 uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980);
291 uint tm_hour = ((dosDate & 0xF800) >> 11);
292 uint tm_min = ((dosDate & 0x7E0) >> 5);
293 uint tm_sec = ((dosDate & 0x1f) << 1);
295 return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec));
298 struct LocalFileHeader
300 uchar signature[4]; // 0x04034b50
301 uchar version_needed[2];
302 uchar general_purpose_bits[2];
303 uchar compression_method[2];
304 uchar last_mod_file[4];
306 uchar compressed_size[4];
307 uchar uncompressed_size[4];
308 uchar file_name_length[2];
309 uchar extra_field_length[2];
312 struct DataDescriptor
315 uchar compressed_size[4];
316 uchar uncompressed_size[4];
319 struct CentralFileHeader
321 uchar signature[4]; // 0x02014b50
322 uchar version_made[2];
323 uchar version_needed[2];
324 uchar general_purpose_bits[2];
325 uchar compression_method[2];
326 uchar last_mod_file[4];
328 uchar compressed_size[4];
329 uchar uncompressed_size[4];
330 uchar file_name_length[2];
331 uchar extra_field_length[2];
332 uchar file_comment_length[2];
334 uchar internal_file_attributes[2];
335 uchar external_file_attributes[4];
336 uchar offset_local_header[4];
337 LocalFileHeader toLocalHeader() const;
340 struct EndOfDirectory
342 uchar signature[4]; // 0x06054b50
344 uchar start_of_directory_disk[2];
345 uchar num_dir_entries_this_disk[2];
346 uchar num_dir_entries[2];
347 uchar directory_size[4];
348 uchar dir_start_offset[4];
349 uchar comment_length[2];
355 QByteArray file_name;
356 QByteArray extra_field;
357 QByteArray file_comment;
360 QZipReader::FileInfo::FileInfo()
361 : isDir(false), isFile(false), isSymLink(false), crc32(0), size(0)
365 QZipReader::FileInfo::~FileInfo()
369 QZipReader::FileInfo::FileInfo(const FileInfo &other)
374 QZipReader::FileInfo& QZipReader::FileInfo::operator=(const FileInfo &other)
376 filePath = other.filePath;
378 isFile = other.isFile;
379 isSymLink = other.isSymLink;
380 permissions = other.permissions;
383 lastModified = other.lastModified;
387 bool QZipReader::FileInfo::isValid() const
389 return isDir || isFile || isSymLink;
395 QZipPrivate(QIODevice *device, bool ownDev)
396 : device(device), ownDevice(ownDev), dirtyFileTree(true), start_of_directory(0)
406 void fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const;
411 QList<FileHeader> fileHeaders;
413 uint start_of_directory;
416 void QZipPrivate::fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const
418 FileHeader header = fileHeaders.at(index);
419 fileInfo.filePath = QString::fromLocal8Bit(header.file_name);
420 const quint32 mode = (qFromLittleEndian<quint32>(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF;
421 fileInfo.isDir = S_ISDIR(mode);
422 fileInfo.isFile = S_ISREG(mode);
423 fileInfo.isSymLink = S_ISLNK(mode);
424 fileInfo.permissions = modeToPermissions(mode);
425 fileInfo.crc32 = readUInt(header.h.crc_32);
426 fileInfo.size = readUInt(header.h.uncompressed_size);
427 fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
430 class QZipReaderPrivate : public QZipPrivate
433 QZipReaderPrivate(QIODevice *device, bool ownDev)
434 : QZipPrivate(device, ownDev), status(QZipReader::NoError)
440 QZipReader::Status status;
443 class QZipWriterPrivate : public QZipPrivate
446 QZipWriterPrivate(QIODevice *device, bool ownDev)
447 : QZipPrivate(device, ownDev),
448 status(QZipWriter::NoError),
449 permissions(QFile::ReadOwner | QFile::WriteOwner),
450 compressionPolicy(QZipWriter::AlwaysCompress)
454 QZipWriter::Status status;
455 QFile::Permissions permissions;
456 QZipWriter::CompressionPolicy compressionPolicy;
458 enum EntryType { Directory, File, Symlink };
460 void addEntry(EntryType type, const QString &fileName, const QByteArray &contents);
463 LocalFileHeader CentralFileHeader::toLocalHeader() const
466 writeUInt(h.signature, 0x04034b50);
467 copyUShort(h.version_needed, version_needed);
468 copyUShort(h.general_purpose_bits, general_purpose_bits);
469 copyUShort(h.compression_method, compression_method);
470 copyUInt(h.last_mod_file, last_mod_file);
471 copyUInt(h.crc_32, crc_32);
472 copyUInt(h.compressed_size, compressed_size);
473 copyUInt(h.uncompressed_size, uncompressed_size);
474 copyUShort(h.file_name_length, file_name_length);
475 copyUShort(h.extra_field_length, extra_field_length);
479 void QZipReaderPrivate::scanFiles()
484 if (! (device->isOpen() || device->open(QIODevice::ReadOnly))) {
485 status = QZipReader::FileOpenError;
489 if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files.
490 status = QZipReader::FileReadError;
494 dirtyFileTree = false;
496 device->read((char *)tmp, 4);
497 if (readUInt(tmp) != 0x04034b50) {
498 qWarning() << "QZip: not a zip file!";
502 // find EndOfDirectory header
504 int start_of_directory = -1;
505 int num_dir_entries = 0;
507 while (start_of_directory == -1) {
508 int pos = device->size() - sizeof(EndOfDirectory) - i;
509 if (pos < 0 || i > 65535) {
510 qWarning() << "QZip: EndOfDirectory not found";
515 device->read((char *)&eod, sizeof(EndOfDirectory));
516 if (readUInt(eod.signature) == 0x06054b50)
522 start_of_directory = readUInt(eod.dir_start_offset);
523 num_dir_entries = readUShort(eod.num_dir_entries);
524 ZDEBUG("start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries);
525 int comment_length = readUShort(eod.comment_length);
526 if (comment_length != i)
527 qWarning() << "QZip: failed to parse zip file.";
528 comment = device->read(qMin(comment_length, i));
531 device->seek(start_of_directory);
532 for (i = 0; i < num_dir_entries; ++i) {
534 int read = device->read((char *) &header.h, sizeof(CentralFileHeader));
535 if (read < (int)sizeof(CentralFileHeader)) {
536 qWarning() << "QZip: Failed to read complete header, index may be incomplete";
539 if (readUInt(header.h.signature) != 0x02014b50) {
540 qWarning() << "QZip: invalid header signature, index may be incomplete";
544 int l = readUShort(header.h.file_name_length);
545 header.file_name = device->read(l);
546 if (header.file_name.length() != l) {
547 qWarning() << "QZip: Failed to read filename from zip index, index may be incomplete";
550 l = readUShort(header.h.extra_field_length);
551 header.extra_field = device->read(l);
552 if (header.extra_field.length() != l) {
553 qWarning() << "QZip: Failed to read extra field in zip file, skipping file, index may be incomplete";
556 l = readUShort(header.h.file_comment_length);
557 header.file_comment = device->read(l);
558 if (header.file_comment.length() != l) {
559 qWarning() << "QZip: Failed to read read file comment, index may be incomplete";
563 ZDEBUG("found file '%s'", header.file_name.data());
564 fileHeaders.append(header);
568 void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const QByteArray &contents/*, QFile::Permissions permissions, QZip::Method m*/)
571 static const char *entryTypes[] = {
575 ZDEBUG() << "adding" << entryTypes[type] <<":" << fileName.toUtf8().data() << (type == 2 ? QByteArray(" -> " + contents).constData() : "");
578 if (! (device->isOpen() || device->open(QIODevice::WriteOnly))) {
579 status = QZipWriter::FileOpenError;
582 device->seek(start_of_directory);
584 // don't compress small files
585 QZipWriter::CompressionPolicy compression = compressionPolicy;
586 if (compressionPolicy == QZipWriter::AutoCompress) {
587 if (contents.length() < 64)
588 compression = QZipWriter::NeverCompress;
590 compression = QZipWriter::AlwaysCompress;
594 memset(&header.h, 0, sizeof(CentralFileHeader));
595 writeUInt(header.h.signature, 0x02014b50);
597 writeUShort(header.h.version_needed, 0x14);
598 writeUInt(header.h.uncompressed_size, contents.length());
599 writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime());
600 QByteArray data = contents;
601 if (compression == QZipWriter::AlwaysCompress) {
602 writeUShort(header.h.compression_method, 8);
604 ulong len = contents.length();
605 // shamelessly copied form zlib
606 len += (len >> 12) + (len >> 14) + 11;
610 res = deflate((uchar*)data.data(), &len, (const uchar*)contents.constData(), contents.length());
617 qWarning("QZip: Z_MEM_ERROR: Not enough memory to compress file, skipping");
624 } while (res == Z_BUF_ERROR);
626 // TODO add a check if data.length() > contents.length(). Then try to store the original and revert the compression method to be uncompressed
627 writeUInt(header.h.compressed_size, data.length());
628 uint crc_32 = ::crc32(0, 0, 0);
629 crc_32 = ::crc32(crc_32, (const uchar *)contents.constData(), contents.length());
630 writeUInt(header.h.crc_32, crc_32);
632 header.file_name = fileName.toLocal8Bit();
633 if (header.file_name.size() > 0xffff) {
634 qWarning("QZip: Filename too long, chopping it to 65535 characters");
635 header.file_name = header.file_name.left(0xffff);
637 writeUShort(header.h.file_name_length, header.file_name.length());
638 //h.extra_field_length[2];
640 writeUShort(header.h.version_made, 3 << 8);
641 //uchar internal_file_attributes[2];
642 //uchar external_file_attributes[4];
643 quint32 mode = permissionsToMode(permissions);
645 case File: mode |= S_IFREG; break;
646 case Directory: mode |= S_IFDIR; break;
647 case Symlink: mode |= S_IFLNK; break;
649 writeUInt(header.h.external_file_attributes, mode << 16);
650 writeUInt(header.h.offset_local_header, start_of_directory);
653 fileHeaders.append(header);
655 LocalFileHeader h = header.h.toLocalHeader();
656 device->write((const char *)&h, sizeof(LocalFileHeader));
657 device->write(header.file_name);
659 start_of_directory = device->pos();
660 dirtyFileTree = true;
663 ////////////////////////////// Reader
666 \class QZipReader::FileInfo
668 Represents one entry in the zip table of contents.
672 \variable FileInfo::filePath
673 The full filepath inside the archive.
677 \variable FileInfo::isDir
678 A boolean type indicating if the entry is a directory.
682 \variable FileInfo::isFile
683 A boolean type, if it is one this entry is a file.
687 \variable FileInfo::isSymLink
688 A boolean type, if it is one this entry is symbolic link.
692 \variable FileInfo::permissions
693 A list of flags for the permissions of this entry.
697 \variable FileInfo::crc32
698 The calculated checksum as a crc32 type.
702 \variable FileInfo::size
703 The total size of the unpacked content.
707 \variable FileInfo::d
717 \brief the QZipReader class provides a way to inspect the contents of a zip
718 archive and extract individual files from it.
720 QZipReader can be used to read a zip archive either from a file or from any
721 device. An in-memory QBuffer for instance. The reader can be used to read
722 which files are in the archive using fileInfoList() and entryInfoAt() but
723 also to extract individual files using fileData() or even to extract all
724 files in the archive using extractAll()
728 Create a new zip archive that operates on the \a fileName. The file will be
729 opened with the \a mode.
731 QZipReader::QZipReader(const QString &archive, QIODevice::OpenMode mode)
733 QScopedPointer<QFile> f(new QFile(archive));
735 QZipReader::Status status;
736 if (f->error() == QFile::NoError)
739 if (f->error() == QFile::ReadError)
740 status = FileReadError;
741 else if (f->error() == QFile::OpenError)
742 status = FileOpenError;
743 else if (f->error() == QFile::PermissionsError)
744 status = FilePermissionsError;
749 d = new QZipReaderPrivate(f.data(), /*ownDevice=*/true);
755 Create a new zip archive that operates on the archive found in \a device.
756 You have to open the device previous to calling the constructor and only a
757 device that is readable will be scanned for zip filecontent.
759 QZipReader::QZipReader(QIODevice *device)
760 : d(new QZipReaderPrivate(device, /*ownDevice=*/false))
768 QZipReader::~QZipReader()
775 Returns device used for reading zip archive.
777 QIODevice* QZipReader::device() const
783 Returns true if the user can read the file; otherwise returns false.
785 bool QZipReader::isReadable() const
787 return d->device->isReadable();
791 Returns true if the file exists; otherwise returns false.
793 bool QZipReader::exists() const
795 QFile *f = qobject_cast<QFile*> (d->device);
802 Returns the list of files the archive contains.
804 QList<QZipReader::FileInfo> QZipReader::fileInfoList() const
807 QList<QZipReader::FileInfo> files;
808 for (int i = 0; i < d->fileHeaders.size(); ++i) {
809 QZipReader::FileInfo fi;
810 d->fillFileInfo(i, fi);
818 Return the number of items in the zip archive.
820 int QZipReader::count() const
823 return d->fileHeaders.count();
827 Returns a FileInfo of an entry in the zipfile.
828 The \a index is the index into the directory listing of the zipfile.
829 Returns an invalid FileInfo if \a index is out of boundaries.
833 QZipReader::FileInfo QZipReader::entryInfoAt(int index) const
836 QZipReader::FileInfo fi;
837 if (index >= 0 && index < d->fileHeaders.count())
838 d->fillFileInfo(index, fi);
843 Fetch the file contents from the zip archive and return the uncompressed bytes.
845 QByteArray QZipReader::fileData(const QString &fileName) const
849 for (i = 0; i < d->fileHeaders.size(); ++i) {
850 if (QString::fromLocal8Bit(d->fileHeaders.at(i).file_name) == fileName)
853 if (i == d->fileHeaders.size())
856 FileHeader header = d->fileHeaders.at(i);
858 int compressed_size = readUInt(header.h.compressed_size);
859 int uncompressed_size = readUInt(header.h.uncompressed_size);
860 int start = readUInt(header.h.offset_local_header);
861 //qDebug("uncompressing file %d: local header at %d", i, start);
863 d->device->seek(start);
865 d->device->read((char *)&lh, sizeof(LocalFileHeader));
866 uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
867 d->device->seek(d->device->pos() + skip);
869 int compression_method = readUShort(lh.compression_method);
870 //qDebug("file=%s: compressed_size=%d, uncompressed_size=%d", fileName.toLocal8Bit().data(), compressed_size, uncompressed_size);
872 //qDebug("file at %lld", d->device->pos());
873 QByteArray compressed = d->device->read(compressed_size);
874 if (compression_method == 0) {
876 compressed.truncate(uncompressed_size);
878 } else if (compression_method == 8) {
880 //qDebug("compressed=%d", compressed.size());
881 compressed.truncate(compressed_size);
883 ulong len = qMax(uncompressed_size, 1);
887 res = inflate((uchar*)baunzip.data(), &len,
888 (uchar*)compressed.constData(), compressed_size);
892 if ((int)len != baunzip.size())
896 qWarning("QZip: Z_MEM_ERROR: Not enough memory");
902 qWarning("QZip: Z_DATA_ERROR: Input data is corrupted");
905 } while (res == Z_BUF_ERROR);
908 qWarning() << "QZip: Unknown compression method";
913 Extracts the full contents of the zip file into \a destinationDir on
914 the local filesystem.
915 In case writing or linking a file fails, the extraction will be aborted.
917 bool QZipReader::extractAll(const QString &destinationDir) const
919 QDir baseDir(destinationDir);
921 // create directories first
922 QList<FileInfo> allFiles = fileInfoList();
923 foreach (FileInfo fi, allFiles) {
924 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
926 if (!baseDir.mkpath(fi.filePath))
928 if (!QFile::setPermissions(absPath, fi.permissions))
934 foreach (FileInfo fi, allFiles) {
935 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
937 QString destination = QFile::decodeName(fileData(fi.filePath));
938 if (destination.isEmpty())
940 QFileInfo linkFi(absPath);
941 if (!QFile::exists(linkFi.absolutePath()))
942 QDir::root().mkpath(linkFi.absolutePath());
943 if (!QFile::link(destination, absPath))
945 /* cannot change permission of links
946 if (!QFile::setPermissions(absPath, fi.permissions))
952 foreach (FileInfo fi, allFiles) {
953 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
956 if (!f.open(QIODevice::WriteOnly))
958 f.write(fileData(fi.filePath));
959 f.setPermissions(fi.permissions);
968 \enum QZipReader::Status
970 The following status values are possible:
972 \value NoError No error occurred.
973 \value FileReadError An error occurred when reading from the file.
974 \value FileOpenError The file could not be opened.
975 \value FilePermissionsError The file could not be accessed.
976 \value FileError Another file error occurred.
980 Returns a status code indicating the first error that was met by QZipReader,
981 or QZipReader::NoError if no error occurred.
983 QZipReader::Status QZipReader::status() const
991 void QZipReader::close()
996 ////////////////////////////// Writer
1003 \brief the QZipWriter class provides a way to create a new zip archive.
1005 QZipWriter can be used to create a zip archive containing any number of files
1006 and directories. The files in the archive will be compressed in a way that is
1007 compatible with common zip reader applications.
1012 Create a new zip archive that operates on the \a archive filename. The file will
1013 be opened with the \a mode.
1016 QZipWriter::QZipWriter(const QString &fileName, QIODevice::OpenMode mode)
1018 QScopedPointer<QFile> f(new QFile(fileName));
1020 QZipWriter::Status status;
1021 if (f->error() == QFile::NoError)
1022 status = QZipWriter::NoError;
1024 if (f->error() == QFile::WriteError)
1025 status = QZipWriter::FileWriteError;
1026 else if (f->error() == QFile::OpenError)
1027 status = QZipWriter::FileOpenError;
1028 else if (f->error() == QFile::PermissionsError)
1029 status = QZipWriter::FilePermissionsError;
1031 status = QZipWriter::FileError;
1034 d = new QZipWriterPrivate(f.data(), /*ownDevice=*/true);
1040 Create a new zip archive that operates on the archive found in \a device.
1041 You have to open the device previous to calling the constructor and
1042 only a device that is readable will be scanned for zip filecontent.
1044 QZipWriter::QZipWriter(QIODevice *device)
1045 : d(new QZipWriterPrivate(device, /*ownDevice=*/false))
1050 QZipWriter::~QZipWriter()
1057 Returns device used for writing zip archive.
1059 QIODevice* QZipWriter::device() const
1065 Returns true if the user can write to the archive; otherwise returns false.
1067 bool QZipWriter::isWritable() const
1069 return d->device->isWritable();
1073 Returns true if the file exists; otherwise returns false.
1075 bool QZipWriter::exists() const
1077 QFile *f = qobject_cast<QFile*> (d->device);
1084 \enum QZipWriter::Status
1086 The following status values are possible:
1088 \value NoError No error occurred.
1089 \value FileWriteError An error occurred when writing to the device.
1090 \value FileOpenError The file could not be opened.
1091 \value FilePermissionsError The file could not be accessed.
1092 \value FileError Another file error occurred.
1096 Returns a status code indicating the first error that was met by QZipWriter,
1097 or QZipWriter::NoError if no error occurred.
1099 QZipWriter::Status QZipWriter::status() const
1105 \enum QZipWriter::CompressionPolicy
1107 \value AlwaysCompress A file that is added is compressed.
1108 \value NeverCompress A file that is added will be stored without changes.
1109 \value AutoCompress A file that is added will be compressed only if that will give a smaller file.
1113 Sets the policy for compressing newly added files to the new \a policy.
1115 \note the default policy is AlwaysCompress
1117 \sa compressionPolicy()
1120 void QZipWriter::setCompressionPolicy(CompressionPolicy policy)
1122 d->compressionPolicy = policy;
1126 Returns the currently set compression policy.
1127 \sa setCompressionPolicy()
1130 QZipWriter::CompressionPolicy QZipWriter::compressionPolicy() const
1132 return d->compressionPolicy;
1136 Sets the permissions that will be used for newly added files.
1138 \note the default permissions are QFile::ReadOwner | QFile::WriteOwner.
1140 \sa creationPermissions()
1143 void QZipWriter::setCreationPermissions(QFile::Permissions permissions)
1145 d->permissions = permissions;
1149 Returns the currently set creation permissions.
1151 \sa setCreationPermissions()
1154 QFile::Permissions QZipWriter::creationPermissions() const
1156 return d->permissions;
1160 Add a file to the archive with \a data as the file contents.
1161 The file will be stored in the archive using the \a fileName which
1162 includes the full path in the archive.
1164 The new file will get the file permissions based on the current
1165 creationPermissions and it will be compressed using the zip compression
1166 based on the current compression policy.
1168 \sa setCreationPermissions()
1169 \sa setCompressionPolicy()
1171 void QZipWriter::addFile(const QString &fileName, const QByteArray &data)
1173 d->addEntry(QZipWriterPrivate::File, fileName, data);
1177 Add a file to the archive with \a device as the source of the contents.
1178 The contents returned from QIODevice::readAll() will be used as the
1180 The file will be stored in the archive using the \a fileName which
1181 includes the full path in the archive.
1183 void QZipWriter::addFile(const QString &fileName, QIODevice *device)
1186 QIODevice::OpenMode mode = device->openMode();
1187 bool opened = false;
1188 if ((mode & QIODevice::ReadOnly) == 0) {
1190 if (! device->open(QIODevice::ReadOnly)) {
1191 d->status = FileOpenError;
1195 d->addEntry(QZipWriterPrivate::File, fileName, device->readAll());
1201 Create a new directory in the archive with the specified \a dirName and
1204 void QZipWriter::addDirectory(const QString &dirName)
1206 QString name = dirName;
1207 // separator is mandatory
1208 if (!name.endsWith(QDir::separator()))
1209 name.append(QDir::separator());
1210 d->addEntry(QZipWriterPrivate::Directory, name, QByteArray());
1214 Create a new symbolic link in the archive with the specified \a dirName
1215 and the \a permissions;
1216 A symbolic link contains the destination (relative) path and name.
1218 void QZipWriter::addSymLink(const QString &fileName, const QString &destination)
1220 d->addEntry(QZipWriterPrivate::Symlink, fileName, QFile::encodeName(destination));
1224 Closes the zip file.
1226 void QZipWriter::close()
1228 if (!(d->device->openMode() & QIODevice::WriteOnly)) {
1233 //qDebug("QZip::close writing directory, %d entries", d->fileHeaders.size());
1234 d->device->seek(d->start_of_directory);
1235 // write new directory
1236 for (int i = 0; i < d->fileHeaders.size(); ++i) {
1237 const FileHeader &header = d->fileHeaders.at(i);
1238 d->device->write((const char *)&header.h, sizeof(CentralFileHeader));
1239 d->device->write(header.file_name);
1240 d->device->write(header.extra_field);
1241 d->device->write(header.file_comment);
1243 int dir_size = d->device->pos() - d->start_of_directory;
1244 // write end of directory
1246 memset(&eod, 0, sizeof(EndOfDirectory));
1247 writeUInt(eod.signature, 0x06054b50);
1248 //uchar this_disk[2];
1249 //uchar start_of_directory_disk[2];
1250 writeUShort(eod.num_dir_entries_this_disk, d->fileHeaders.size());
1251 writeUShort(eod.num_dir_entries, d->fileHeaders.size());
1252 writeUInt(eod.directory_size, dir_size);
1253 writeUInt(eod.dir_start_offset, d->start_of_directory);
1254 writeUShort(eod.comment_length, d->comment.length());
1256 d->device->write((const char *)&eod, sizeof(EndOfDirectory));
1257 d->device->write(d->comment);
1263 #endif // QT_NO_TEXTODFWRITER